单词
题目描述
小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。
输入输出格式
输入格式:
第一行一个整数N,表示有N个单词。接下来N行每行一个单词,每个单词都由小写字母(a-z)组成。(N≤200)
输出格式:
输出N个整数,第i行的数表示第i个单词在文章中出现了多少次。
输入输出样例
说明
数据范围
30%的数据, 单词总长度不超过10^3
100%的数据,单词总长度不超过10^6
分析:
此题有多种做法,$hash$(太麻烦不想打),后缀数组(不会),$AC$自动机(就是你了)。为了复习一波$AC$自动机,蒟蒻选择了此种做法。
实际上也算是比较明显的$AC$自动机了,在做的时候记录路径就可以轻松过了,思路就不多赘述,蒟蒻就只放代码了。
Code:
//It is made by HolseLee on 10th Aug 2018 //Luogu.org P3966 #include<bits/stdc++.h> using namespace std; const int N=202; const int L=1e6+7; int n,ans[N],t[L][26],tot,all,val[L],fail[L],lis[L],mk[L]; char s[L+N],a[L]; queue<int>T; struct Trie{ void ins(int num) { int len=strlen(a),root=0,v; for(int i=0;i<len;++i){ s[all++]=a[i]; } s[all++]='#'; for(int i=0;i<len;++i){ v=a[i]-'a'; if(!t[root][v])t[root][v]=++tot; root=t[root][v]; } if(!val[root])val[root]=num; mk[num]=val[root]; } void build() { int u=0,v; for(int i=0;i<26;++i){ if(t[u][i])T.push(t[u][i]); } while(!T.empty()){ u=T.front();T.pop(); for(int i=0;i<26;++i){ v=t[u][i]; if(v){ fail[v]=t[fail[u]][i]; lis[v]=val[fail[v]]?fail[v]:lis[fail[v]]; T.push(v); } else t[u][i]=t[fail[u]][i]; } } } void quary() { int u=0,v; for(int i=0;i<all;++i){ if(s[i]=='#'){ u=0;continue; } u=t[u][s[i]-'a']; if(val[u])ans[val[u]]++; v=lis[u]; while(v){ ans[val[v]]++; v=lis[v]; } } } }ac; int main() { scanf("%d",&n); for(int i=1;i<=n;++i){ scanf("%s",a); ac.ins(i); } ac.build();ac.quary(); for(int i=1;i<=n;++i) printf("%d ",ans[mk[i]]); return 0; }