web-dev-qa-db-ja.com

std :: mapのキーの部分一致

を持っています std::mapそして部分文字列を使用してキーを検索したい。たとえば、次のコードがあります。

#include <iostream>
#include <map>
#include <string>
using namespace std;

typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;

int main(int argc, char *argv[])
{
    TStrStrMap tMap;

    tMap.insert(TStrStrPair("John", "AA"));
    tMap.insert(TStrStrPair("Mary", "BBB"));
    tMap.insert(TStrStrPair("Mother", "A"));
    tMap.insert(TStrStrPair("Marlon", "C"));

    return 0;
}

ここで、「Marla」がマップに格納されている場合、「Marlon」ではなく「Marl」という部分文字列を保持する位置を検索したいと思います。 「マール」で始まるものを見つけたいです。私はせいぜい1つの位置を見つける必要があります。これは可能ですか?もしそうなら、どのように?

Boostライブラリは使いたくない!

24
cateof

部分文字列を効率的に検索することはできませんが、プレフィックスを検索することはできます:

#include <iostream>
#include <map>
#include <string>
#include <algorithm>
using namespace std;

typedef map<string, string> TStrStrMap;
typedef pair<string, string> TStrStrPair;

TStrStrMap::const_iterator FindPrefix(const TStrStrMap& map, const string& search_for) {
    TStrStrMap::const_iterator i = map.lower_bound(search_for);
    if (i != map.end()) {
        const string& key = i->first;
        if (key.compare(0, search_for.size(), search_for) == 0) // Really a prefix?
            return i;
    }
    return map.end();
}

void Test(const TStrStrMap& map, const string& search_for) {
    cout << search_for;
    auto i = FindPrefix(map, search_for);
    if (i != map.end())
        cout << '\t' << i->first << ", " << i->second;
    cout << endl;
}

int main(int argc, char *argv[])
{
    TStrStrMap tMap;

    tMap.insert(TStrStrPair("John", "AA"));
    tMap.insert(TStrStrPair("Mary", "BBB"));
    tMap.insert(TStrStrPair("Mother", "A"));
    tMap.insert(TStrStrPair("Marlon", "C"));

    Test(tMap, "Marl");
    Test(tMap, "Mo");
    Test(tMap, "ther");
    Test(tMap, "Mad");
    Test(tMap, "Mom");
    Test(tMap, "Perr");
    Test(tMap, "Jo");

    return 0;
}

これは印刷します:

Marl    Marlon, C
Mo      Mother, A
ther
Mad
Mom
Perr
Jo      John, AA
27

例のように部分文字列がprefixの場合、 lower_bound を使用して"Marl"を検索できます。

    map<string,string>::const_iterator m = tMap.lower_bound("Marl");
    cerr << (*m).second << endl;

これは、プレフィックス以外の部分文字列では機能しません。一般的な場合、マップの検索は他のコンテナーの検索と大差ありません。

9
dasblinkenlight

マップ内のキーの部分文字列を検索するには、特別な種類のキータイプで新しいマップを使用するか、O(n)でマップを検索する以外に選択肢はありません。 _std::map_は、(デフォルトで)operator<()をキーの順序付けと検索に使用し、_std::string_の比較関数は単純な辞書式比較です。

部分文字列に基づいてoperator<() compareを持つ特別なキータイプで新しいマップを作成する場合、これは挿入する新しい要素が重複するかどうかの決定にも影響することに注意してください。言い換えると、そのようなマップには、相互の部分文字列ではない要素のみが含まれます。

O(n)検索は、実際には、マップ上でstd::find()を使用することを意味し、カスタム述語は_std::pair<std::string,std::string>_を取り、の2番目の要素がtrueを返す場合にtrueを返します。ペアは最初の部分文字列です。

2
wilhelmtell

map::lower_bound() を使用して完全なソリューションを提供することにより、 dasblinkenlightによる回答 を拡張したいと思います。その回答のコメントで述べたように、lower_bound()tMap.end()を返すかどうかを確認する必要があります。そうでない場合は、見つかったキーの前に検索文字列が実際に付いているかどうかも確認する必要があります。 string::compare() を使用して、後者を確認できます。 たとえば 。その結果、私のC++ 11ソリューションは次のようになります。

std::map<std::string, std::string> myMap{
    {"John", "AA"}, {"Mary", "BBB"}, {"Mother", "A"}, {"Marlon", "C"}, {"Marla", "D"}
};
std::string prefix("Marl");

auto it = myMap.lower_bound(prefix);
if (it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0)
    std::cout << it->first << ": " << it->second << std::endl;

出力:

マーラ:D

ただし、検索文字列のプレフィックスが付いたマップ内のすべてのキーを検索する場合は、次のループを使用できます。

for (auto it = myMap.lower_bound(prefix); it != std::end(myMap) && it->first.compare(0, prefix.size(), prefix) == 0; ++it)
    std::cout << it->first << ": " << it->second << std::endl;

出力:

マーラ:D
マーロン:C

Ideoneのコード

1
honk
typedef TStrStrMap::value_type map_value_type;

struct key_contains_substring
   : std::binary_function<map_value_type, std::string, bool>
{
    bool operator()(const map_value_type& map_value, const std::string& substr)
    {
         return std::search(map_value.first.begin(), map_value.first.end(),
                    substr.begin(), substr.end() != map_value.first.end());  
    }
};

...


TStrStrMap::const_iterator it = std::find_if(tMap.begin(), tMap.end(), 
        std::bind2nd(key_contains_substring(), "Marl");
1
Armen Tsirunyan