C ++中的前缀匹配

时间:2018-08-01 20:19:40

标签: c++ regex string stl substring

问题:

假设我有一个前缀列表:

[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'<'b进行测试,两者都必须为假)。因此std :: compare()函数在这种情况下不起作用,因为检查一个字符串是否为另一字符串的前缀是不对称关系。

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

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

3 个答案:

答案 0 :(得分:1)

这取决于您的目标。

如果您有很多前缀并且只有一个“目标”,那么代码就是最佳选择。

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

构建该结构可能需要一些时间,但是如果使用中有很多“目标”,那将是有回报的。

答案 1 :(得分:0)

您打算有多少个前缀?如果小于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

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

答案 2 :(得分:0)

这是您要找的东西吗?

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

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