【问题标题】:Partially match long key in std::map部分匹配 std::map 中的长键
【发布时间】:2013-10-07 21:24:39
【问题描述】:

我在我的项目中使用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'

我希望你明白我在追求什么。我想有效地检索映射到与比它们大的比较键相对应的键的值,但共享相同的前缀(例如“测试”)。

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

注意:我曾尝试使用带有 std::greater_equal 作为键比较器的映射与 lower_bound 方法相结合,但最终会出现运行时错误,并且我也质疑这种方法的效率。

【问题讨论】:

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


【解决方案1】:

以下内容将满足您的需求:

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";
}

【讨论】:

  • 这不会找到完全匹配的,对吧?例如,您无法直接使用此机制找到test,只能找到test + 其他任何形式的字符串。我不清楚 OP 的真正要求是什么。
  • 我对精确匹配和“大于”匹配感兴趣。另外,这个解决方案是否有效?我需要在商业 3D 游戏的每一帧中执行其中的几个操作。
  • @ElliottDarfink 恕我直言,该解决方案是高效的,因为它使用 O(log n) 进行一次查找(与 find() 的复杂性相同)。是的,你需要额外的检查,但这些都是 O(1) (忽略字符串的长度)。您可能需要稍微调整它以适应您的用例,但lower_bound(甚至upper_bound?)是这里的方法,因为它会给您一个迭代器,它至少非常接近您正在寻找的元素.
  • @DanielFrey 如果您到达地图的末尾(顺便说一句,如果 lower_bound() 返回 map.end() 会出现错误)并且 s 不匹配您将遍历整个地图的任何键。我怀疑这是否非常有效。
  • @Slave 修复了这个错误。不过,我不明白“遍历整个地图”部分。我不迭代,我的代码中没有循环。您只需执行固定数量的操作,每个操作都 O(log n) 或 O(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 这样的解析器生成器来获得更有效的解决方案。

【讨论】:

    【解决方案3】:

    如果您不介意更改地图中元素的顺序,这应该可行并且相当简单:

        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";
        }
    

    【讨论】:

      【解决方案4】:

      使用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

      注意:这段代码展示了一堆不同的方法来做同样的事情。只使用你需要的东西。我个人认为最后一个是最简单和最容易阅读的,因为它不会将代码分散到很远的地方。

      【讨论】:

        猜你喜欢
        • 2012-03-10
        • 2023-03-29
        • 2014-07-15
        • 1970-01-01
        • 2023-01-13
        • 2016-08-28
        • 1970-01-01
        • 2021-11-14
        • 1970-01-01
        相关资源
        最近更新 更多