C++中的前缀匹配

Prefix Matching in C++

本文关键字:前缀 C++      更新时间:2023-10-16

问题:

假设我有一个前缀列表:

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

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

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

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

如果你有很多前缀,只有一个"目标",那么你的代码就是最优的。

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

建造这座建筑可能需要一些时间,但如果使用有很多"目标",就会有回报

您计划使用多少个可能的前缀?如果它小于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

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

这就是您想要的吗?

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

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