题
题解
AC自动机坑题
build的时候不要累加end
如果这个点是结尾,就把end设为1,否则就是0
每次匹配完一个字符,就顺着fail查询所有的end加起来,同时将已经计算过的end设为-1,以后只要查到了-1,就直接停止,这样保证每个节点只被访问了一次
PS:后缀数组做法:先把所有字符串串起来,然后O(N)扫描出每个小串和大串的最长公共前缀,只要等于该小串的长度,就cnt++,总复杂度O(N)
代码
//AC自动机
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
int fail[500010], ed[500010], trie[500010][30], tot;
char s[1000100];
queue<int> q;
void insert(char *s)
{
int pos;
for(pos=1;*s;s++)pos=trie[pos][*s-97]?trie[pos][*s-97]:trie[pos][*s-97]=++tot;
ed[pos]++;
}
void acabuild()
{
int u, f, i;
q.push(1);
while(!q.empty())
{
u=q.front(),q.pop();
for(i=0;i<26;i++)
if(trie[u][i])
{
for(f=fail[u];f and !trie[f][i];f=fail[f]);
if(f)fail[trie[u][i]]=trie[f][i];
else fail[trie[u][i]]=1;
q.push(trie[u][i]);
}
}
}
void acamove(int &pos, int x)
{
for(;pos and !trie[pos][x];pos=fail[pos]);
pos=pos?trie[pos][x]:1;
}
int main()
{
int T, N, ans, i, pos, x, cnt=0;
char *p;
scanf("%d",&T);
while(T--)
{
tot=1;
memset(fail,0,sizeof(fail));
memset(ed,0,sizeof(ed));
memset(trie,0,sizeof(trie));
scanf("%d",&N);
for(i=2;i<=tot;i++)fail[i]=1;
for(i=1;i<=N;i++)scanf("%s",s),insert(s);
acabuild();
scanf("%s",s);
for(pos=1,ans=0,p=s;*p;p++)
{
acamove(pos,*p-97);
for(x=pos;ed[x]!=-1;x=fail[x])ans+=ed[x],ed[x]=-1;
}
printf("%d\n",ans);
}
return 0;
}