在std :: map中部分匹配长键

时间:2013-10-01 16:05:36

标签: c++ algorithm c++11 map std

我在项目中使用std::map,因为我想要将几个不同的字符串相互映射。例如,我可能会创建一个类似于此的地图:

std::map<std::string, std::string> map;

map["test"] = "Foo";
map["blah"] = "Drei";
map["fayh"] = "Najh";
// And so on...

我想使用比地图中的键长的键来找到这些值,即部分匹配键。地图中的所有键与它们与

进行比较的键共享相同的前缀

这就是我想要实现的目标:

// Same map as earlier
std::cout << map.find('test123').second;    // Should output 'Foo'
std::cout << map.find('test_2165').second;  // Should output 'Foo' as well
std::cout << map.find('tes').second;        // Key not found
std::cout << map.find('fayh_TK_Ka').second; // 'Najh'

我希望你明白我的意思。我想有效地检索映射到键的值,这些键对应于比它们大的比较键,但共享相同的前缀(例如'test')。

我不知道std::map在这种情况下是否是最佳选择,如果没有,请告诉其他选项。

注意:我尝试使用std::greater_equal作为关键比较器和lower_bound方法的地图,但我最终遇到了运行时错误,我也质疑这种方法的效率。

4 个答案:

答案 0 :(得分:5)

以下将满足您的需求:

std::string my_find( const std::string& s )
{
    auto it = map.lower_bound( s );
    if( it != map.begin() ) {
        if( it != map.end() && it->first == s ) return it->second; // exact match
        --it;
        if( it->first == s.substr( 0, it->first.size() ) ) {
            return it->second;
        }
    }
    return "Key not found";
}

答案 1 :(得分:2)

这段代码非常有效:

#include <algorithm>
#include <vector>
#include <iostream>

typedef std::pair<std::string, std::string> Strings;
typedef std::vector<Strings> Values;

std::string findValue( const Values &vals, const std::string &str )
{
   auto it = std::lower_bound( vals.begin(), vals.end(), Strings( str, std::string() ), []( const Strings &s1, const Strings &s2 ) {
      size_t len = std::min( s1.first.length(), s2.first.length() );
      return s1.first.substr( 0, len ) < s2.first.substr( 0, len );
   } );
   if( it == vals.end() || it->first.length() > str.length() ) return std::string();
   return it->second;
}

void test(  const Values &vals, const std::string &str )
{
   std::cout << "testing \"" << str << "\" - \"" << findValue( vals, str ) << "\"" << std::endl;
}

int main()
{
    Values vals { { "test", "Foo" }, { "blah", "Drei" }, { "fayh", "Najh" } };
    std::sort( vals.begin(), vals.end(), []( const Strings &s1, const Strings &s2 ) { return s1.first < s2.first; } );

    test( vals, "test123" );
    test( vals, "test_2165" );
    test( vals, "test" );
    test( vals, "tes" );

    return 0;
}

对于更有效的解决方案,如果您的数据当然是静态的,可以使用像lex这样的解析器生成器。

答案 2 :(得分:0)

如果你不介意改变地图中元素的顺序,这应该有效并且非常简单:

    std::map<std::string, std::string, std::greater<std::string>> map;
    map["test"] = "Foo";
    map["blah"] = "Drei";
    map["fayh"] = "Najh";

    std::string s = "fay";

    auto i = map.lower_bound(s);
    if (i != map.end() && std::equal(i->first.begin(), i->first.end(), s.begin())) {
        std::cout << i->first << " -> " << i->second << "\n";
    } else {
        std::cout << "not found\n";
    }

答案 3 :(得分:0)

使用std::map能力从std::map外部定义比较器,如下所示:

#include <iostream>
#include <map>
#include <string>
#include <string.h>
#include <functional>

using namespace std;

// Define comparator functor:
struct functor_test
{
    bool operator()(string const & lhs, string const & rhs)
    {
        return strncmp(lhs.c_str(), rhs.c_str(), 4) < 0;
    }
};

// Define comparator function:
bool function_test(string const & lhs, string const & rhs)
{
    return strncmp(lhs.c_str(), rhs.c_str(), 4) < 0;
}

int main() {
    // Define comparator lambda:
    auto lambda_test = [](string const & lhs, string const & rhs)
    {
        return strncmp(lhs.c_str(), rhs.c_str(), 4) < 0;
    };

    // These are all valid ways to declare the key comparitor:

    //As a functor:
    //  map<string, string, functor_test>                                 map;

    //As a function using a function reference type:
    //  map<string, string, bool(&)(string const&, string const&)>        map(function_test);

    //As a function using a function pointer type:
    //  map<string, string, bool(*)(string const&, string const&)>        map(function_test);

    //As a function using a function class type wrapper:
    //  map<string, string, function<bool(string const&, string const&)>> map(function_test);

    //As a predefined lambda:
    //  map<string, string, decltype(lambda_test)>                        map(lambda_test);

    //As a predefined lambda using a function class type wrapper:
    //  map<string, string, function<bool(string const&, string const&)>> map(lambda_test);

    //As a lambda using a function class type wrapper:
    map<string, string, function<bool(string const&, string const&)>>   map(
        [](string const & lhs, string const & rhs)
        {
            return strncmp(lhs.c_str(), rhs.c_str(), 4) < 0;
        });

    map["test"] = "Foo";
    map["blah"] = "Drei";
    map["fayh"] = "Najh";

    std::cout << map.find("test123")->second << endl;    // Should output 'Foo'
    std::cout << map.find("test_2165")->second << endl;  // Should output 'Foo' as well
    if (map.find("tes") == map.end())
    {
        cout << "Not found" << endl;
    }// Key not found
    std::cout << map.find("fayh_TK_Ka")->second << endl; // 'Najh'

    return 0;
}

要使用工作演示,请参阅此处:http://ideone.com/sg4sET

注意:此代码显示了一堆不同的方法来执行相同的操作。只使用你需要的东西。我个人认为最后一个是最简单易读的,因为它不会远离它的使用而散布代码。