什么是最有效的通配符字符串匹配算法?我只询问一个想法,没有必要提供实际的代码。
我认为这样的算法可以使用已排序的后缀数组构建,这可以产生O(log(n))的性能。
我说错了吗?
编辑:
我指的是"A*B"
,"*sip*"
或"A?B"
等模式,其中星号表示任意数量的符号,问号表示单个符号。
答案 0 :(得分:4)
这里有一篇论文介绍了最快的选项 http://swtch.com/~rsc/regexp/regexp1.html 特别是它允许你避免在使用长模式时变得病态慢的天真算法。
它涵盖了通用正则表达式,但您可以将实现限制为所需的子集。
答案 1 :(得分:2)
嗯,我认为正常的模式匹配规则适用于此处。通常,由于您拥有数据流和短模式,因此您不需要实现比线性更有效的方法。但是,模式越长,优化的空间就越大。
你有什么样的通配符?单字符通配符(例如正则表达式中的.
)或多字符通配符(例如.*
)?有限制吗?什么是预期的模式长度,您是否对要检查的数据进行随机或串行访问?
答案 2 :(得分:2)
我一直在寻找一种在多项式时间内运行的简单通配符匹配算法。例如。这个很简单,但是当模式包含许多星(*)时,它不会在多项式时间内运行:http://www.codeproject.com/Articles/188256/A-Simple-Wildcard-Matching-Function 下面是使用动态编程将时间复杂度降低到O(n * m)的代码,其中n是文本的长度,m是模式的长度。
#include <string>
#include <vector>
#include <algorithm>
using namespace std;
const int UNKNOWN = -1;
const int NOMATCH = 0;
const int MATCHES = 1;
class Wildcard {
string _text;
string _pattern;
vector<vector<int>> _mf;
int F(int n, int m) {
if (_mf[n][m] >= 0) return _mf[n][m];
if (n == 0 && m == 0) {
_mf[n][m] = MATCHES;
return _mf[n][m];
}
if (n > 0 && m == 0) {
_mf[n][m] = NOMATCH;
return _mf[n][m];
}
// m > 0
int ans = NOMATCH;
if (_pattern[m - 1] == '*') {
ans = max(ans, F(n, m-1));
if (n > 0) {
ans = max(ans, F(n - 1, m));
}
}
if (n > 0) {
if (_pattern[m - 1] == '?' || _pattern[m - 1] == _text[n - 1]) {
ans = max(ans, F(n - 1, m - 1));
}
}
_mf[n][m] = ans;
return _mf[n][m];
}
public:
bool match(string text, string pattern) {
_text = text;
_pattern = pattern;
_mf.clear();
for (int i = 0; i <= _text.size(); i++) {
_mf.push_back(vector<int>());
for (int j = 0; j <= _pattern.size(); j++) {
_mf[i].push_back(UNKNOWN); // not calculated
}
}
int ans = F(_text.size(), _pattern.size());
return ans == MATCHES;
}
};
答案 3 :(得分:1)
如果您的模式只能包含*
通配符,则可以尽可能快地执行。在这种情况下要实现的主要事情是,您只需要查找每张卡片一次(卡片=星星之间的片段)。
这是一个实现(仅支持*
通配符):
#include <cstddef>
#include <cstring>
#include <algorithm>
#include <string>
#include <vector>
#include <iostream>
using namespace std;
class wildcard_pattern {
public:
explicit wildcard_pattern(const string& text);
bool match(const char* begin, const char* end) const;
bool match(const char* c_str) const;
private:
string m_text;
struct card {
size_t m_offset, m_size;
card(size_t begin, size_t end);
};
// Must contain at least one card. The first, and the last card
// may be empty strings. All other cards must be non-empty. If
// there is exactly one card, the pattern matches a string if, and
// only if the string is equal to the card. Otherwise, the first
// card must be a prefix of the string, and the last card must be
// a suffix.
vector<card> m_cards;
};
wildcard_pattern::wildcard_pattern(const string& text):
m_text(text)
{
size_t pos = m_text.find('*');
if (pos == string::npos) {
m_cards.push_back(card(0, m_text.size()));
return;
}
m_cards.push_back(card(0, pos));
++pos;
for (;;) {
size_t pos_2 = m_text.find('*', pos);
if (pos_2 == string::npos)
break;
if (pos_2 != pos)
m_cards.push_back(card(pos, pos_2));
pos = pos_2 + 1;
}
m_cards.push_back(card(pos, m_text.size()));
}
bool wildcard_pattern::match(const char* begin, const char* end) const
{
const char* begin_2 = begin;
const char* end_2 = end;
size_t num_cards = m_cards.size();
typedef string::const_iterator str_iter;
// Check anchored prefix card
{
const card& card = m_cards.front();
if (size_t(end_2 - begin_2) < card.m_size)
return false;
str_iter card_begin = m_text.begin() + card.m_offset;
if (!equal(begin_2, begin_2 + card.m_size, card_begin))
return false;
begin_2 += card.m_size;
}
if (num_cards == 1)
return begin_2 == end_2;
// Check anchored suffix card
{
const card& card = m_cards.back();
if (size_t(end_2 - begin_2) < card.m_size)
return false;
str_iter card_begin = m_text.begin() + card.m_offset;
if (!equal(end_2 - card.m_size, end_2, card_begin))
return false;
end_2 -= card.m_size;
}
// Check unanchored infix cards
for (size_t i = 1; i != num_cards-1; ++i) {
const card& card = m_cards[i];
str_iter card_begin = m_text.begin() + card.m_offset;
str_iter card_end = card_begin + card.m_size;
begin_2 = search(begin_2, end_2, card_begin, card_end);
if (begin_2 == end_2)
return false;
begin_2 += card.m_size;
}
return true;
}
inline bool wildcard_pattern::match(const char* c_str) const
{
const char* begin = c_str;
const char* end = begin + strlen(c_str);
return match(begin, end);
}
inline wildcard_pattern::card::card(size_t begin, size_t end)
{
m_offset = begin;
m_size = end - begin;
}
int main(int, const char* argv[])
{
wildcard_pattern pat(argv[1]);
cout << pat.match(argv[2]) << endl;
}
答案 4 :(得分:0)
性能不仅取决于要搜索的字符串的长度,还取决于查询字符串中通配符的数量(和类型)。如果您被允许使用匹配任意数量字符的*
,包括整个文档,并且您可以拥有任意数量的星标,这将对可能获得的内容设置一些限制。
如果你可以在O(f(n))时间内确定匹配某个字符串foo
,那么查询foo_0*foo_1*foo_2*...*foo_m
将需要O(m * f(n))时间,其中m是*
通配符的数量。
答案 5 :(得分:0)
根据通配符“语言”,我(可能)只需编写一个通配符&gt; regexp编译器并使用(通常提供的)regexp引擎进行实际匹配。这有点懒,但除非有这样做的实际性能问题,否则它足够快。
答案 6 :(得分:0)
您可以将通配符查询转换为正则表达式并使用它来匹配; RE总是可以转换为DFA(离散有限自动机),那些是有效的(线性时间)和一个小常数。
答案 7 :(得分:0)
O(n log m)是正确的。见http://www.cs.bris.ac.uk/Publications/Papers/2000602.pdf
希望这会有所帮助......
答案 8 :(得分:0)
这是C语言中的一个简单实现,它利用指针并尝试对字符串进行单次遍历,并在可能的情况下通配符模式在一般简单情况下获得最大的效率。请注意,它还避免了用于字符串的任何函数,例如“ length()”(可能因语言而异),这些函数可能会遍历字符串并增加不必要的计算时间。
#include <stdio.h>
_Bool wildcard_strcmp(char *line, char *pattern)
{
_Bool wildcard = 0;
char *placeholder;
do
{
if ((*pattern == *line) || (*pattern == '?'))
{
line++;
pattern++;
}
else if (*pattern == '*')
{
if (*(++pattern) == '\0')
{
return 1;
}
wildcard = 1;
}
else if (wildcard)
{
if (pattern == placeholder)
{
line++;
}
else
{
pattern = placeholder;
}
}
else
{
return 0;
}
} while (*line);
if (*pattern == '\0')
{
return 1;
}
else
{
return 0;
}
}
int main()
{
char string[200] = "foobarfoobar";
char pattern[200] = "fo?*barfoo*";
if (wildcard_strcmp(string, pattern))
{
printf("Match\n");
}
else
{
printf("No Match\n");
}
return 0;
}