算法大致过程及分析
前几天瞎搞了ac自动机模板,本文将ac自动机算法较重要的地方及实现细节记录下来。
AC自动机是著名的多串模式匹配算法。
形式上说,就是一堆模式串,一个文本串,可以将模式串与文本串匹配。
大体实现就是将所有模式串存到一个trie里,匹配就在trie上面走,若失配则走失配指针到一个最优的能继续匹配的结点。
当然,在匹配的时候,会有下面这种情况需要特殊判断
盗个图,哈(
此图原出处)
模式串有ac,abc,bc
文本串为acabc
那么匹配了abc之后,bc就被忽略了,但事实上也要被匹配到。
那么这些遗漏的串都是abc的后缀,所以要沿着fail链往上找。
这里有一个小优化
我们就可以在求fail链的时候顺带求出一个点fail链上最近一个单词末尾的结点,设为up[v]
那么匹配了v点就沿着up链往上走即可。
并不知道效果怎么样。
L表示模式串总长度,N表示文本串总长度
时间复杂度是O(L+N)
本蒟蒻并不会证明,感性理解吧
因为不可能每一次都能沿fail链跳很多步,跳着跳着就跳到根了,要再有跳很多步的机会又要匹配很多字符
上面一行纯属扯淡
代码实现及注意事项
- 个人习惯把1设为trie的根
- trie的存储依题而定,或许可以26个指针,有时卡空间的话要用模拟链表
- 如果多组数据要记得清空tot,trie[][],标记数组,最好不要memset
- 建fail参见下面代码
void bfs()
{
q.push(1);
while(!q.empty())
{
int v=q.front();q.pop();
fo(i,0,25)
if(trie[v][i])
{
int x=trie[v][i],y=fail[v];
while(y && !trie[y][i]) y=fail[y];
if(y) fail[x]=trie[y][i];
else fail[x]=1;
q.push(x);
}
}
}
匹配是这个样子的
int x=1;
fo(i,1,len)
{
int y=s[i]-'a';
while(x!=1 && !trie[x][y]) x=fail[x];
if(trie[x][y]) x=trie[x][y];
for(int v=x;v!=1;v=fail[v])
{
}
}