【问题标题】:Partial match for the key of a std::map部分匹配 std::map 的键
【发布时间】:2012-03-10 02:59:33
【问题描述】:

我有一个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”存储在地图中,我想搜索包含子字符串“Marl”而不是“Marlon”的位置。我想找到以“Marl”开头的东西。我最多需要找到一个位置。这可能吗?如果是这样,怎么做?

我不想使用任何 Boost 库!

【问题讨论】:

  • 等一下,“Marl 而不是 Marlon” 表示您在搜索 Marl 时不想找到 Marlon?
  • @wilhelmtell 想找到以 Marl 开头的东西。也许不是 Marlon,例如 Marla 存储在地图中。我最多需要找到一个位置。

标签: c++ dictionary search containers stdmap


【解决方案1】:

我想通过使用map::lower_bound() 提供完整的解决方案来扩展answer by Sergey。正如该答案的 cmets 中所述,您必须检查 lower_bound() 是否返回 tMap.end()。如果不是,那么您还必须检查找到的键是否实际上以搜索字符串为前缀。可以使用string::compare() 来检查后者,for example。结果,我的 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

Code on Ideone

【讨论】:

    【解决方案2】:
    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");
    

    【讨论】:

    • 不编译。 no matching function for call to 'search(std::__cxx11::basic_string&lt;char&gt;::const_iterator, std::__cxx11::basic_string&lt;char&gt;::const_iterator, std::__cxx11::basic_string&lt;char&gt;::const_iterator, bool)'
    • @xamid。是放错了括号。固定
    • 第一部分就是这样编译的。在"Marl" 之后仍然缺少一些右括号。然后,为了编译第二部分,我还必须将 operator() 设为常量,即bool operator()(...) const {
    【解决方案3】:

    你不能有效地搜索子字符串,但是你可以搜索前缀

    #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
    

    【讨论】:

      【解决方案4】:

      要在映射中搜索键的子字符串,您别无选择,只能在特殊类型的键类型上使用新映射或在 O(n) 中搜索您的映射。 std::map 使用(默认情况下)operator&lt;() 对键进行排序和搜索,std::string 的比较函数是一个普通的字典比较。

      如果您在具有operator&lt;() 基于子字符串比较的特殊键类型上创建新映射,请注意这也会影响确定要插入的新元素是否为重复元素。换句话说,这样的映射只会包含不是彼此的子字符串的元素。

      O(n) 搜索实际上意味着您在地图上使用 std::find(),并带有一个自定义谓词,该谓词采用 std::pair&lt;std::string,std::string&gt; 并在该对的第二个元素是第一个元素的子字符串时返回 true。

      【讨论】:

        【解决方案5】:

        当您的子字符串是示例中的 前缀 时,您可以使用 lower_bound 搜索 "Marl"

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

        这不适用于非前缀子字符串:在一般情况下,搜索地图与搜索其他容器没有太大区别。

        【讨论】:

        • 这回答了示例中的问题,“Marlon”的“Marl”,但不是搜索子字符串的一般问题。
        • @wilhelmtell 绝对,你是对的。我更新了答案以提及这一点。当搜索 map 不同于搜索列表时,搜索前缀子字符串是唯一的情况。
        • 良好的开端,但仅此还不够。您还需要检查找到的字符串是否实际上带有搜索字符串的前缀(它可能只是在字典上更大,但前缀不同)。
        • @BrankoDimitrijevic 当然,这不是一个完整的解决方案,只是一个框架,暗示了如何实现真正的解决方案。尽管绝对必要,但附加逻辑隐藏了解决方案的“牛肉”。这就是为什么我跳过了其他必要的检查来查看返回值是否在tMap.end()
        猜你喜欢
        • 2013-10-07
        • 2023-03-29
        • 2014-07-15
        • 2023-01-13
        • 2016-08-28
        • 1970-01-01
        • 2018-12-20
        • 2020-10-05
        • 1970-01-01
        相关资源
        最近更新 更多