题目
有 NN 个由小写字母组成的模式串以及一个文本串 TT 。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 TT 中出现的次数最多。
https://www.luogu.org/problemnew/show/P3796
思路
trie树打错了,改了我几个小时。。。
其实,AC自动机只是在Trie树上做KMP罢了。
- 建树(不多说)
- 构建fail指针。Trie树的失配指针是指向:沿着其父节点 的 失配指针,一直向上,直到找到拥有当前这个字母的子节点 的节点 的那个子节点。复杂???对,我也是这么想的。看多几遍代码吧。
- 查询(不多说)
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1e6+77;
struct AC
{
int vis[27],fail,end;
}ac[maxn];
int n,cnt=0;
void build(char *c)
{
int l=strlen(c),u=0;
for(int i=0; i<l; i++)
{
char x=c[i];
if(!ac[u].vis[x-'a']) ac[u].vis[x-'a']=++cnt;
u=ac[u].vis[x-'a'];
}
ac[u].end+=1;
}
void getfail()
{
queue<int> q;
for(int i=0; i<26; i++)
{
if(ac[0].vis[i])
{
ac[ac[0].vis[i]].fail=0;
q.push(ac[0].vis[i]);
}
}
while(!q.empty())
{
int u=q.front(); q.pop();
for(int i=0; i<26; i++)
{
if(ac[u].vis[i])
{
ac[ac[u].vis[i]].fail=ac[ac[u].fail].vis[i];
q.push(ac[u].vis[i]);
}else
ac[u].vis[i]=ac[ac[u].fail].vis[i];
}
}
}
int ACM(char *st)
{
int l=strlen(st),u=0,ass=0;
for(int i=0; i<l; i++)
{
u=ac[u].vis[st[i]-'a'];
for(int t=u; t&&ac[t].end!=-1; t=ac[t].fail)
{
ass+=ac[t].end; ac[t].end=-1;
}
}
return ass;
}
int main()
{
char c[maxn];
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%s",c);
build(c);
}
ac[0].fail=0;
getfail();
scanf("%s",c);
printf("%d",ACM(c));
}