描述
http://www.lydsy.com/JudgeOnline/problem.php?id=3172
(n)个单词组成一篇文章,求每个单词在文章中出现的次数.
分析
这道题很像BZOJ_2434_[NOI2011]_阿狸的打字机_(AC自动机+dfs序+树状数组)
一个单词出现过,那么一定是某个单词的某个前缀的后缀,可以通过这个前缀的末尾沿着失配边找到它.我们要统计有多少点可以这样找到它.
建立fail树,很显然,单词(x)子树中的所有点都可以沿着失配边找到(x),这样我们只用记录每个点有多少次就行了.
(eg:如果文章是 a aa aaa,那么a有3次(三个单词各一次),aa有2次(第二个单词和第三个单词),aaa有1次(第三个单词)).

1 #include <bits/stdc++.h> 2 using namespace std; 3 const int maxn=1e6+10,type=26; 4 int n,m; 5 char s[maxn]; 6 struct Aho_Corasick{ 7 int sz; 8 int q[maxn],val[maxn],pos[maxn],f[maxn]; 9 int ch[maxn][type]; 10 Aho_Corasick():sz(0){memset(ch[0],0,sizeof ch[0]);memset(val,0,sizeof val);} 11 inline int insert(char *s){ 12 int u=0,m=strlen(s+1); 13 for(int i=1;i<=m;i++){ 14 int c=s[i]-'a'; 15 if(!ch[u][c]){ 16 memset(ch[++sz],0,sizeof ch[sz]); val[sz]=0; 17 ch[u][c]=sz; 18 } 19 u=ch[u][c]; 20 val[u]++; 21 } 22 return u; 23 } 24 inline void get_fail(){ 25 int L=1,R=0; 26 for(int c=0;c<type;c++){ 27 int u=ch[0][c]; 28 if(u){f[u]=0;q[++R]=u;} 29 } 30 while(L<=R){ 31 int u=q[L++]; 32 for(int c=0;c<type;c++){ 33 int t=ch[u][c]; 34 if(!t){ch[u][c]=ch[f[u]][c];continue;} 35 int v=f[u]; 36 f[t]=ch[v][c]; 37 q[++R]=t; 38 } 39 } 40 for(int i=sz;i;i--) val[f[q[i]]]+=val[q[i]]; 41 } 42 inline void solve(){ 43 for(int i=1;i<=n;i++) printf("%d ",val[pos[i]]); 44 } 45 }ac; 46 inline void solve(){ 47 ac.get_fail(); 48 ac.solve(); 49 } 50 inline void init(){ 51 scanf("%d",&n); 52 for(int i=1;i<=n;i++){ 53 scanf("%s",s+1); 54 ac.pos[i]=ac.insert(s); 55 } 56 } 57 int main(){ 58 init(); 59 solve(); 60 return 0; 61 }
3172: [Tjoi2013]单词
Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 2837 Solved: 1356
[Submit][Status][Discuss]
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