AC自动机
不得不说fail树太神奇了。
每次插入一个单词时把所有路径上的点都+1,每个单词的出现次数就是其结尾节点的fail树子树和。因为其fail指针指向的节点一定包含当前字符串。
统计贡献时要从下往上加,所以最好BFS的时候保存其BFS序后倒着搞。
代码:
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;
int n,nd,t[N][26],nxt[N],f[N],id[N],p[N];
char s[N];
queue <int> q;
inline void nsrt(int id){
int x=0;
for (int i=0;s[i];i++){
int ch=s[i]-'a';
if (!t[x][ch]) t[x][ch]=++nd;
x=t[x][ch],f[x]++;
}
p[id]=x;
}
inline void build(){
while (!q.empty()) q.pop(); nd=0;
for (int i=0,v;i<26;i++)
if (v=t[0][i]) q.push(v),id[v]=++nd;
while (!q.empty()){
int x=q.front(),fa=nxt[x]; q.pop();
for (int i=0,v;i<26;i++)
if (v=t[x][i])
q.push(v),id[v]=++nd,nxt[v]=t[fa][i];
else t[x][i]=t[fa][i];
}
}
int main(){
scanf("%d",&n);
for (int i=1;i<=n;i++) scanf("%s",s),nsrt(i);
build();
for (int i=nd;i;i--) f[nxt[id[i]]]+=f[id[i]];
for (int i=1;i<=n;i++) printf("%d\n",f[p[i]]);
return 0;
}