首先建一棵Trie,我们考虑AC自动机中fail指针的含义。
与KMP中的fail指针相似,它指向的位置代表了一个相同前后缀。所以我们首先给每个点打标记,即每个前缀(root->此点)都出现了一次,然后在构造完fail指针后,我们会发现fail指针反向后是一棵树 沿着fail指针扫一遍其实就是沿着树边向根扫一遍。只在插入时将每个串的每个节点sum++ 那么每个串终点所在fail树的子树中sum的总和就是这个字符串的出现次数,只要根据BFS序求和就好了。
据说这题的作法很多?
#include<bits/stdc++.h>
using namespace std;
char s[1000005];
int n,cnt=1,pos[222],a[1000005][26],sum[1000005],p[1000005],q[1000005];
inline void insert(int &pos)
{
scanf("%s",s);
int l=strlen(s),x=1,c;
for (int i=0;i<l;i++)
{
c=s[i]-'a';
if (a[x][c]) x=a[x][c]; else x=a[x][c]=++cnt;
sum[x]++;
}
pos=x;
}
inline void build_fail()
{
int t=0,w=1,x;
q[1]=1; p[1]=0;
while (t<w)
{
x=q[++t];
for (int i=0;i<26;i++)
if (a[x][i])
{
int k=p[x];
while (!a[k][i]) k=p[k];
p[a[x][i]]=a[k][i];
q[++w]=a[x][i];
}
}
for (int i=w;i;i--) sum[p[q[i]]]+=sum[q[i]];
}
int main()
{
scanf("%d",&n);
for (int i=0;i<26;i++) a[0][i]=1;
for (int i=1;i<=n;i++) insert(pos[i]);
build_fail();
for (int i=1;i<=n;i++) printf("%d\n",sum[pos[i]]);
return 0;
}