[TJOI2013]单词(AC自动机+前缀和维护)
作者:互联网
链接:https://ac.nowcoder.com/acm/problem/20443
来源:牛客网
题目描述
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。输入描述:
第一个一个整数N,表示有多少个单词,接下来N行每行一个单词。每个单词由小写字母组成,N ≤ 200,单词长度不超过10^6
输出描述:
输出N个整数,第i行的数字表示第i个单词在文章中出现了多少次。
具体思路:首先在trie树上每次添加一个节点,这个节点的权值就+1,保存每个字符串最终节点在trie树上的编号。然后再去建立fail指针。当建立好fail指针的时候,跑一遍前缀和。
具体跑前缀和的时候,每一个节点更新到这个节点的fail指针上就好了。
AC代码:
1 #include<bits/stdc++.h> 2 using namespace std; 3 # define ll long long 4 const int mod = 1e9+7; 5 const int maxn = 1e6+100; 6 int ch[maxn][30],tot=0; 7 char str[200+10][maxn]; 8 int val[maxn]; 9 int ans[maxn]; 10 int add_trie(char st[]) 11 { 12 int len=strlen(st); 13 int p=0; 14 for(int i=0; i<len; i++) 15 { 16 int to=st[i]-'a'; 17 if(!ch[p][to]) 18 ch[p][to]=++tot; 19 p=ch[p][to]; 20 val[p]++; 21 } 22 return p; 23 } 24 int fail[maxn],last[maxn]; 25 int sto[maxn]; 26 int num=0; 27 void get_fail() 28 { 29 queue<int>q; 30 for(int i=0; i<=tot; i++) 31 fail[i]=-1; 32 q.push(0); 33 while(!q.empty()) 34 { 35 int top=q.front(); 36 q.pop(); 37 for(int i=0; i<26; i++) 38 { 39 int u=ch[top][i]; 40 if(u==0) 41 continue; 42 sto[++num]=u; 43 q.push(u); 44 int v=fail[top]; 45 while(v!=-1&&!ch[v][i]) 46 v=fail[v]; 47 fail[u]=(v==-1?0:ch[v][i]); 48 last[u]=val[fail[u]]?fail[u]:last[fail[u]]; 49 } 50 } 51 } 52 int n; 53 void init() 54 { 55 for(int i=num; i>=1; i--) 56 { 57 val[fail[sto[i]]]+=val[sto[i]]; 58 } 59 } 60 void print() 61 { 62 for(int i=1; i<=n; i++) 63 { 64 printf("%d\n",val[ans[i]]); 65 } 66 } 67 int main() 68 { 69 scanf("%d",&n); 70 for(int i=1; i<=n; i++) 71 { 72 scanf("%s",str[i]); 73 ans[i] = add_trie(str[i]); 74 } 75 get_fail(); 76 init(); 77 print(); 78 return 0; 79 }
标签:单词,AC,前缀,val,int,TJOI2013,maxn,fail,节点 来源: https://www.cnblogs.com/letlifestop/p/10877056.html