http://www.elijahqi.win/archives/2888
Description
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
Input
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6
Output
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
Sample Input
3
a
aa
aaa
Sample Output
6
3
1
HINT
Source
这题主要还是利用了AC自动机fail树的一种性质 什么性质呢 就是我这个节点如果出现了 那么就会在我fail树上所有到根的路径上出现 那么不妨在构造AC自动机的时候 就给节点++ 然后反向建出fail树 然后dfs一下 即可得到答案 这次文章的意思是把给定的所有单词中间用特殊字符连接起来就是文章了..
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1010000
using namespace std;
int cnt=1;char s[N];int h[N],fail[N];
int trans[N][26],ans[N],num,end[220],n;
inline void insert1(char s[],int id){
int len=strlen(s+1),p=1;
for (int i=1,nxt;i<=len;++i){
if (!trans[p][s[i]-'a']) trans[p][s[i]-'a']=nxt=++cnt;
else nxt=trans[p][s[i]-'a'];p=nxt;++ans[p];
}end[id]=p;
}
struct node{
int y,next;
}data[N];
inline void insert1(int x,int y){
data[++num].y=y;data[num].next=h[x];h[x]=num;
}
inline void dfs(int x){
for (int i=h[x];i;i=data[i].next){
int y=data[i].y;dfs(y);ans[x]+=ans[y];
}
}
inline void buildAC(){
queue<int>q;q.push(1);for (int i=0;i<26;++i) trans[0][i]=1;
while(!q.empty()){
int x=q.front();q.pop();
for (int i=0;i<26;++i){
int &y=trans[x][i];
if (y) fail[y]=trans[fail[x]][i],q.push(y);
else {y=trans[fail[x]][i];continue;}insert1(fail[y],y);
}
}
}
int main(){
// freopen("bzoj3172.in","r",stdin);
scanf("%d",&n);
for (int i=1;i<=n;++i) scanf("%s",s+1),insert1(s,i);buildAC();dfs(1);
for (int i=1;i<=n;++i) printf("%d\n",ans[end[i]]);
return 0;
}