【问题标题】:Prefix Matching in C++C++ 中的前缀匹配
【发布时间】:2019-01-09 12:29:05
【问题描述】:

问题:

假设我有一个前缀列表:

[p1, p2, p3, ... pn] //Prefix List (strings)

我想知道字符串 'target' 是否有任何上述前缀。

朴素的解决方案示例:

bool contains_prefix(std::string target, vector<std::string> &prefixes)
{
    for (const auto& prefix : prefixes)
    {
       if (target.compare(0, prefix.length(), prefix)
           return true;
    }
    return false;
}

std::vector<std::string> prefixes{"car" , "auto" , "biscuits"};

bool test = contains_prefix("automobile", prefixes); //returns true
test = contains_prefix("biscu", prefixes); //returns false
test = contains_prefix("v", prefixes); //returns false (obviously)

因此,这种简单的解决方案有一个明显的缺陷,即它必须遍历列表中的每一项。

有没有更快的方法来实现这种类型的前缀匹配?

我试过了:

1. 我尝试创建一个与 std::set 一起使用的比较对象,但集合需要严格的弱排序(通过 a>b 和 a'

2.我可以使用正则表达式来实现,但这并不能解决必须遍历每个元素的问题。

3.任何散列的数据结构都不适用于基于模式的匹配。

【问题讨论】:

标签: c++ regex string stl substring


【解决方案1】:

这取决于你的目标是什么。

如果您有很多前缀而只有一个“目标”,那么您的代码就是最优的。

但是,如果您有很多“目标”,那么您可能需要考虑创建一个更智能的结构,而不仅仅是一个前缀列表。我建议使用前缀树。 https://en.wikipedia.org/wiki/Trie

构建结构可能需要一些时间,但如果使用有很多“目标”,它会得到回报。

【讨论】:

    【解决方案2】:

    您计划有多少个可能的前缀?如果它小于 50,那么我认为没有什么可以优化的。

    如果您使用任何优化,请进行性能测量以确保您获得任何收益。对于少量前缀,更复杂的解决方案会更慢。

    最简单的解决方案是使用树,这是在set 中实现的,所以这应该可以解决问题(它具有o(log n) 复杂性):

    // needed since we want longer prefixes before 
    // the shorter one with same begging
    class StrCmpRevAlphaLongerFirst {
    public:
        bool operator()(const std::string &a, const std::string &b) const {
            return !std::lexicographical_compare(a.begin(), a.end(),
                                                 b.begin(), b.end(),
                                                 std::greater_equal<char>()
                                                );
        }
    };
    
    using PrefixSet = std::set<std::string, StrCmpRevAlphaLongerFirst>;
    
    bool contains_prefix(const std::string& target, const PrefixSet &prefixes)
    {
        auto it = prefixes.lower_bound(target);
        return it->length() <= target.length() 
            && std::equal(it->begin(), it->end(), target.begin());
    }
    

    https://wandbox.org/permlink/hoskfQxh6nr2BLq7

    在特殊情况下,我认为可以实现恒定时间(通过使用哈希函数)。例如,如果您有很多长度有限的前缀,您可以使用std::unordered_set

    class PrefixMatcher {
    public:
        PrefixMatcher(const std::unordered_set<std::string> &prefixes)
            : m_prefixes(prefixes)
        {
             for (const auto &s : m_prefixes) {
                  m_lengths.insert(s.length());
             }
        }
    
        bool machesPrefix(const std::string& target) const {
             for (auto length : m_lengths) {
                 if (target.length() < length) {
                     continue;
                 }
                 std::string prefix{ target.begin(), target.begin() + length };
                 if (m_prefixes.count(prefix) == 1) {
                     return true;
                 }
             }
             return false;
        }
    
    private:
        std::unordered_set<std::string> m_prefixes;
        std::set<size_t> m_lengths;    
    };
    

    https://wandbox.org/permlink/fXnO3GHimtram6Lo

    如果前缀的可能长度数量有限,则上述解决方案具有恒定的时间复杂度。

    【讨论】:

      【解决方案3】:

      这就是你要找的吗?

      #include <iostream>
      #include <string>
      #include <regex>
      using std::string;
      using std::regex;
      using std::cout;
      
      bool contains_prefix(const string& target, const string& prefixes)
      {
          return std::regex_match(target, regex(prefixes));
      }
      
      int main()
      {
          string target = "automobile";
          if (contains_prefix(target, "(car)(.*)|(auto)(.*)|(biscuits)(.*)"))
              std::cout << "The target has prefix.\n";
          else
              std::cout << "The target has no prefix.\n";
      
          return 0;
      }
      

      如果您有很多前缀,您可以考虑将它们转换为单个正则表达式字符串。

      【讨论】:

        猜你喜欢
        • 2012-01-29
        • 1970-01-01
        • 1970-01-01
        • 2011-07-23
        • 2019-01-11
        • 2013-06-03
        • 1970-01-01
        • 1970-01-01
        • 2011-10-05
        相关资源
        最近更新 更多