单词(bzoj 3172)
Description
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
Input
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N<=200,单词长度不超过10^6
Output
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
Sample Input
3
a
aa
aaa
a
aa
aaa
Sample Output
6
3
1
3
1
/* 在构建trie树时,统计每个节点出现的次数。 从前向后DP,用一个单词的后缀的次数来更新前缀的次数。 */ #include<cstdio> #include<iostream> #include<cstring> #define N 1000010 using namespace std; int a[N][26],point[N],sum[N],q[N],w[210],h,t,n,size=1; char s[N]; void insert(int &x){ int len=strlen(s),now=1; for(int i=0;i<len;i++){ int t=s[i]-'a'; if(!a[now][t])a[now][t]=++size; now=a[now][t]; sum[now]++; } x=now; } void acmach(){ h=0;t=1;q[1]=1; while(h<t){ int now=q[++h]; for(int i=0;i<=25;i++){ if(!a[now][i])continue; int k=point[now]; while(!a[k][i])k=point[k]; point[a[now][i]]=a[k][i]; q[++t]=a[now][i]; } } } void solve(){ for(int i=t;i;i--)sum[point[q[i]]]+=sum[q[i]]; for(int i=1;i<=n;i++)printf("%d\n",sum[w[i]]); } int main(){ scanf("%d",&n); for(int i=0;i<=25;i++)a[0][i]=1; for(int i=1;i<=n;i++){ scanf("%s",&s); insert(w[i]); } acmach();solve(); return 0; }