这个东西挺神奇的,巧妙的把KMP搬到了trie上
说一说最重要的失配函数
这里和KMP不一样,KMP的失配函数是链接到自己前面的某个位置,而AC自动机的则是trie的其他子树上,意义和KMP倒是一样,那么如何实现呢,我们通过一次对trie的bfs实现
void getfail()
{
queue<int>q;f[0]=0;
for(int i=0;i<26;i++)
if(ch[0][i])f[ch[0][i]]=0,q.push(ch[0][i]);// 很显然,第一层节点的指针一定指向总根
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<26;i++)
{
int u=ch[now][i];
if(!u)ch[now][i]=ch[f[now]][i];// 如果不存在就有点意思,为了防止f函数跳的次数过多,
// 我们直接将trie树该节点的i儿子连向失配指针指向的地方,可以做到加速的作用
else q.push(u),f[u]=ch[f[now]][i];// 因为是bfs,所以当处理到这一层的时候,他的父
// 节点的f函数一定预处理过了,那么,可以直接连接到它父节点的失配指针的下一层
// 这样可以达到一层层深入的目的
}
}
}
可能会有几个问题,我们画个图
我们先看最下面的h,跑到它的时候,它的父节点t的失配指针指向0点,此时h的指针就为它父节点 t的h号儿子
我们再看右下方的那个e,在跑到它的时候,它的父节点h已经得出了失配函数为上方的h
这时它的失配函数就是它父节点失配指针的e号儿子
重复上面的过程即可
匹配的话,就比较简单,因为有了那个在trie上加边的操作,甚至连while都不用写…
void find(string a)
{
int len=a.length();
int j=0;
for(int i=0;i<len;i++)
{
j=ch[j][a[i]-'a'];
for(int t=j;t;t=f[t])ans[E[t]].num+=End[t];
}
}
忽略奇怪的东西,上两题代码
洛谷P3796 【模板】AC自动机(加强版)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 5e5+7;
int ch[maxn][26],cnt;
int n,End[maxn],E[maxn];
void putin(string a,int pos)
{
int root=0;
for(int i=0;i<a.length();i++)
{
if(!ch[root][a[i]-'a'])ch[root][a[i]-'a']=++cnt;
root=ch[root][a[i]-'a'];
}
End[root]++;
E[root]=pos;
}
int last[maxn],f[maxn];
void getfail()
{
queue<int>q;f[0]=0;
for(int i=0;i<26;i++)
if(ch[0][i])f[ch[0][i]]=0,q.push(ch[0][i]);
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<26;i++)
{
int u=ch[now][i];
if(!u)ch[now][i]=ch[f[now]][i];
else q.push(u),f[u]=ch[f[now]][i];
}
}
}
struct out
{
int pos,num;
friend bool operator <(out a,out b)
{
if(a.num!=b.num)return a.num>b.num;
else return a.pos<b.pos;
};
}ans[200];
void find(string a)
{
int len=a.length();
int j=0;
for(int i=0;i<len;i++)
{
j=ch[j][a[i]-'a'];
for(int t=j;t;t=f[t])ans[E[t]].num+=End[t];
}
}
string s[200];
int main()
{
while(1)
{
scanf("%d",&n);
if(n==0)break;
for(int i=1;i<=n;i++)
{
string into;
cin>>into;
s[i]=into;
putin(into,i);
ans[i].pos=i;
}
getfail();
string T;
cin>>T;
find(T);
sort(ans+1,ans+1+n);
cout<<ans[1].num<<endl<<s[ans[1].pos]<<endl;
for(int i=2;i<=n;i++)
{
if(ans[i].num==ans[i-1].num)cout<<s[ans[i].pos]<<endl;
else break;
}
memset(ans,0,sizeof(ans));
memset(f,0,sizeof(f));
memset(End,0,sizeof(End));
memset(E,0,sizeof(E));
memset(ch,0,sizeof(ch));
}
return 0;
}
洛谷P3808 【模板】AC自动机(简单版)
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
using namespace std;
const int maxn = 5e6+7;
int ch[maxn][26],cnt;
int n,end[maxn];
void putin(string a)
{
int root=0;
for(int i=0;i<a.length();i++)
{
if(!ch[root][a[i]-'a'])ch[root][a[i]-'a']=++cnt;
root=ch[root][a[i]-'a'];
}
end[root]++;
}
int last[maxn],f[maxn];
void getfail()
{
queue<int>q;f[0]=0;
for(int i=0;i<26;i++)
if(ch[0][i])f[ch[0][i]]=0,q.push(ch[0][i]);
while(!q.empty())
{
int now=q.front();q.pop();
for(int i=0;i<26;i++)
{
int u=ch[now][i];
if(!u)ch[now][i]=ch[f[now]][i];
else q.push(u),f[u]=ch[f[now]][i];
}
}
}
void find(string a)
{
int len=a.length();
int j=0,ans=0;
for(int i=0;i<len;i++)
{
j=ch[j][a[i]-'a'];
for(int t=j;t&&~end[t];t=f[t])ans+=end[t],end[t]=-1;
}
cout<<ans;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
string into;
cin>>into;
putin(into);
}
getfail();
string T;
cin>>T;
find(T);
return 0;
}
二次加强版还没写,有点麻烦…鬼知道等啥时闲下来吧