C++中的前缀匹配
Prefix Matching in C++
问题:
假设我有一个前缀列表:
[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;
}
如果有许多前缀,可以考虑将它们转换为单个正则表达式字符串。
- 编译器如何在前缀和 postix 运算符之间进行区分?
- 查找带有 Anaconda cmake 前缀的 boost-python3
- 迭代器类的重载前缀增量运算符会引发分段错误
- 如何在友元函数中使用静态成员而不添加前缀 [类名]::
- 生成前缀位掩码
- 定义宏以将前缀 0x 添加到十六进制字符串文本
- CMake 错误 - 目标 foo INTERFACE_SOURCES属性包含在源目录中以前缀为前缀的路径
- 如何在自定义对象的<<运算符中添加自定义前缀
- 高效的字符串截断算法,按顺序删除相等的前缀和后缀
- 以C++显示单词的所有前缀
- QXmlStreamWriter,命名空间和前缀
- 为什么 C++ 程序员更喜欢前缀 ++,而 Java 程序员更喜欢后缀 ++?
- 为什么 libclang 会错误解析带有 .h 前缀C++标头?
- 目标是找到两个 c 字符串之间的公共前缀(必须使用特定的函数标头)
- 我需要在C++的两个字符串之间找到共同的前缀
- 更新 Visual Studio 2017,现在出现编译错误 C7510:"回调":使用依赖模板名称必须以 'template' 为前缀
- 将单个字符转换为 std::string 前缀 \x01
- 如果我有很多具有相似前缀的字符串,是否有理由从该前缀创建一个子字符串?
- 有没有办法在不使用命名空间 std 或前缀 std:: 的情况下引用 cout?
- 提升精神解析字符串以前缀开头