问题:
假设我有一个前缀列表:
[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。。任何基于散列的数据结构均不适用于基于模式的匹配。
答案 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;
}
如果您有许多前缀,则可以考虑将其转换为单个正则表达式字符串。