​AC自动机模板题:keywords search ​

网上说AC自动机的前置技能需要学习字典树和KMP,可我的感觉和KMP关系不太大,和字典树的数据结构类型关系倒是有密切联系,一定是我太菜了,所以理解不是那么深刻

AC自动机的关键:失配指针的构造

​fail​​​指针的构造是由​​bfs()​​​分层遍历完成的,首先我们先将第一层压入队列,第一层的​​fail[]:0节点​​,然后寻找下一层的失配指针,下一层的失配指针,在上一层父亲的失配指针的孩子是否有和下一层相同的孩子,如果有则是,如果没有则往上找,直到0节点结束,这里的思想和KMP的类似,新层的点需要压入队列,而旧层的点用过,删除掉即可

AC代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int maxn=1e6+5;
const int alph=26;
int tir[500005][26];//前缀树
int fail[500005];//fail指针
char str[maxn],s[maxn];
int sum[500005];
int cnt;//计数器
void inser(char*str)
{
int len=strlen(str);
int root=0;
for(int i=0; i<len; i++)
{
int id=str[i]-'a';
if(!tir[root][id])
{
tir[root][id]=++cnt;
}
root=tir[root][id];
}
sum[root]++;
}
void getfail()//构造fail指针
{
queue<int>q;
int root=0;
fail[0]=0;
for(int i=0; i<alph; i++)
{
if(tir[root][i])
{
fail[tir[root][i]]=0;
q.push(tir[root][i]);
}
}
while(!q.empty())
{
//printf("1\n");
int r=q.front();
q.pop();
for(int i=0;i<alph;i++)
{

int u=tir[r][i];
if(u)
{
q.push(u);
int v=fail[r];
while(v&&!tir[v][i])
v=fail[v];
fail[u]=tir[v][i];
}
}
}
}
int match(char *s)
{
int len=strlen(s);
int root=0;
int ans=0;
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
while(root&&!tir[root][c])
root=fail[root];
root=tir[root][c];
int tem=root;
while(tem&&sum[tem])
{
ans+=sum[tem];
sum[tem]=0;
tem=fail[tem];
}
}

return ans;
}
int main()
{

int t;
scanf("%d",&t);
while(t--)
{
int n;
cnt=0;
scanf("%d",&n);
for(int i=1; i<=n; i++)
{
scanf("%s",str);
inser(str);
}
getfail();
scanf("%s",s);
printf("%d\n",match(s));
memset(sum,0,sizeof(sum));
memset(tir,0,sizeof(tir));
}
}