C++ 在Aho-Corasick自动机中寻找循环
我正面临着一个问题,这个问题应该通过使用Aho Corasick自动机来解决。我得到了一组单词(由“0”或“1”组成)-模式,我必须决定是否有可能创建无限文本,其中不包含任何给定模式。我认为,解决方案是创建一个Aho Corasick自动机并搜索一个没有匹配状态的循环,但我不能提出一个好的方法来做到这一点。我曾想过使用DFS搜索状态图,但我不确定它是否有效,我有一个实现问题-让我们假设,我们处于一个状态,该状态有一个“1”边-但该边指向的状态被标记为匹配-因此我们不能使用该边,我们可以尝试失败链接(当前状态没有“0”边)-但我们还必须记住,我们不能使用当前状态的失败链接所指向状态的“1”边缘 有人能纠正我并告诉我怎么做吗?我用C++编写了Aho Corasick,我确信它是有效的,我也理解整个算法。 以下是基本代码:C++ 在Aho-Corasick自动机中寻找循环,c++,algorithm,C++,Algorithm,我正面临着一个问题,这个问题应该通过使用Aho Corasick自动机来解决。我得到了一组单词(由“0”或“1”组成)-模式,我必须决定是否有可能创建无限文本,其中不包含任何给定模式。我认为,解决方案是创建一个Aho Corasick自动机并搜索一个没有匹配状态的循环,但我不能提出一个好的方法来做到这一点。我曾想过使用DFS搜索状态图,但我不确定它是否有效,我有一个实现问题-让我们假设,我们处于一个状态,该状态有一个“1”边-但该边指向的状态被标记为匹配-因此我们不能使用该边,我们可以尝试失败链
class AhoCorasick
{
static const int ALPHABET_SIZE = 2;
struct State
{
State* edge[ALPHABET_SIZE];
State* fail;
State* longestMatchingSuffix;
//Vector used to remember which pattern matches in this state.
vector< int > matching;
short color;
State()
{
for(int i = 0; i < ALPHABET_SIZE; ++i)
edge[i] = 0;
color = 0;
}
~State()
{
for(int i = 0; i < ALPHABET_SIZE; ++i)
{
delete edge[i];
}
}
};
private:
State root;
vector< int > lenOfPattern;
bool isFailComputed;
//Helper function used to traverse state graph.
State* move(State* curr, char letter)
{
while(curr != &root && curr->edge[letter] == 0)
{
curr = curr->fail;
}
if(curr->edge[letter] != 0)
curr = curr->edge[letter];
return curr;
}
//Function which computes fail links and longestMatchingSuffix.
void computeFailLink()
{
queue< State* > Q;
root.fail = root.longestMatchingSuffix = 0;
for(int i = 0; i < ALPHABET_SIZE; ++i)
{
if(root.edge[i] != 0)
{
Q.push(root.edge[i]);
root.edge[i]->fail = &root;
}
}
while(!Q.empty())
{
State* curr = Q.front();
Q.pop();
if(!curr->fail->matching.empty())
{
curr->longestMatchingSuffix = curr->fail;
}
else
{
curr->longestMatchingSuffix = curr->fail->longestMatchingSuffix;
}
for(int i = 0; i < ALPHABET_SIZE; ++i)
{
if(curr->edge[i] != 0)
{
Q.push(curr->edge[i]);
State* state = curr->fail;
state = move(state, i);
curr->edge[i]->fail = state;
}
}
}
isFailComputed = true;
}
public:
AhoCorasick()
{
isFailComputed = false;
}
//Add pattern to automaton.
//pattern - pointer to pattern, which will be added
//fun - function which will be used to transform character to 0-based index.
void addPattern(const char* const pattern, int (*fun) (const char *))
{
isFailComputed = false;
int len = strlen(pattern);
State* curr = &root;
const char* pat = pattern;
for(; *pat; ++pat)
{
char tmpPat = fun(pat);
if(curr->edge[tmpPat] == 0)
{
curr = curr->edge[tmpPat] = new State;
}
else
{
curr = curr->edge[tmpPat];
}
}
lenOfPattern.push_back(len);
curr->matching.push_back(lenOfPattern.size() - 1);
}
};
int alphabet01(const char * c)
{
return *c - '0';
}
class属性
{
静态常数int字母表大小=2;
结构状态
{
状态*边缘[字母表大小];
国家*失败;
状态*最长匹配uffix;
//用于记住在此状态下匹配的模式的向量。
向量匹配;
短颜色;
国家()
{
对于(int i=0;ilenOfPattern;
bool-isFailComputed;
//用于遍历状态图的辅助函数。
状态*移动(状态*当前,字符字母)
{
while(curr!=&root&&curr->edge[字母]==0)
{
当前=当前->失败;
}
如果(当前->边缘[字母]!=0)
当前=当前->边缘[字母];
返回货币;
}
//计算失败链接和最长匹配uffix的函数。
void computeFailLink()
{
队列Q;
root.fail=root.longestMatchingSuffix=0;
对于(int i=0;ifail=&root;
}
}
而(!Q.empty())
{
状态*curr=Q.front();
Q.pop();
如果(!curr->fail->matching.empty())
{
curr->longestmachingsuffix=curr->fail;
}
其他的
{
curr->longestmachingsuffix=curr->fail->longestmachingsuffix;
}
对于(int i=0;i边缘[i]!=0)
{
推送(电流->边缘[i]);
状态*状态=当前->失败;
状态=移动(状态,i);
电流->边缘[i]->故障=状态;
}
}
}
isFailComputed=true;
}
公众:
阿霍科拉西克()
{
isFailComputed=false;
}
//向自动机添加模式。
//模式-指向将要添加的模式的指针
//fun-用于将字符转换为基于0的索引的函数。
void addPattern(常量字符*常量模式,int(*fun)(常量字符*)
{
isFailComputed=false;
int len=strlen(模式);
状态*curr=&root;
常量字符*pat=模式;
对于(;*pat;++pat)
{
char tmpPat=乐趣(pat);
如果(当前->边缘[tmpPat]==0)
{
当前=当前->边缘[tmpPat]=新状态;
}
其他的
{
当前=当前->边缘[tmpPat];
}
}
Lenof模式。推回(len);
curr->matching.push_back(lenOfPattern.size()-1);
}
};
int alphabet01(常量字符*c)
{
返回*c-'0';
}
我没有仔细阅读您的代码,但我知道非常简单高效的实现
首先,让我们向树添加字典后缀链接(它们的描述可以在维基百科中找到)。然后,您必须查看所有树,并以某种方式将匹配节点和具有Dict后缀链接的节点标记为坏节点。对这些操作的解释是显而易见的:您不需要所有匹配的节点,也不需要包含匹配后缀的节点
现在有了一个没有任何匹配节点的Aho-Corasick树。如果您只是在生成的树上运行DFS algo,您将得到您想要的 请发布一份构建自动机的代码草图(请不要完整的代码-只需关键部分):在构建自动机时,修改代码以查找循环可能很容易。我不确定字典后缀链接是什么意思,但我非常确定,我完全按照你说的去做-在你回复前几天,我已经实现了类似的东西-但它不起作用-几天后我意识到了原因-我正在使用只使用一个字母构建的循环。做得好。字典后缀链接指的是指向前一个后缀的链接,即完整单词。