ac自动机是kmp在trie上的推广,kmp适用于单模式串匹配,而ac自动机适用于多模式串的匹配。既然是kmp在trie上的推广,我们不妨先来回忆一下kmp的核心:求next数组的过程。next[i]是与模式串前缀匹配的后缀最长的长度(也可以理解为与最长后缀匹配的前缀的下标)此处说的都是非平凡的前缀后缀。
next[0]=next[1]=0;
for(int i=2;i<=n;i++)
{
j=next[i-1];
while(j&&p[j+1]!=p[i]) j=next[j];
if(p[j+1]==p[i]) j++;
next[i]=j;
}
推广到trie树上,是基于bfs,将前面层的信息都算出来。、
int hh=0,tt=-1;
for(int i=0;i<26;i++)
if(tr[0][i])
q[++tt]=tr[0][i];
while(hh<=tt)
{
int t=q[hh ++ ];
for(int i=0;i<26;i++)
{
int c=tr[t][i];
if(!c) continue;
int j=next[t];
while(j && !tr[j][i]) j=next[j];
if(tr[j][i]) j=tr[j][i];
next[c]=j;
q[++tt]=c;
}
}
同样匹配的时候我们也先看一下kmp是怎么做的
for(int i=1,j=0;i<=n;i++)
{
while(j&&s[i]!=p[j+1]) j=ne[j];
if(s[i]==p[j+1]) j++;
if(j==m)
{
j=ne[j];
//匹配成功
}
}
根据这个推广到ac自动机上:
for(int i=0,j=0;i<n;i++)
{
int t=str[i]-'a';
while(j&&!tr[j][t]) j=ne[j];
if(tr[j][t]) j=tr[j][t];
int p=j;
while(p)
{
res+=cnt[p];
cnt[p]=0;
p=ne[p];
}
}
trie图优化: 如果不存在,指向父节点next指针的对应儿子 存在的话,更新ne[c]
if(!c) tr[t][i]=tr[ne[t]][i];
else
{
ne[c]=tr[ne[t]][i];
q[++tt]=c;
}