[TJOI2013]单词 AC 自动机
题目描述:
小张最近在忙毕设,所以一直在读论文。
一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。
题解:
AC 自动机裸题,将所有字符串读入到一个数组里,字符串之间用 “#” 隔开即可。
Code:
#include<string> #include<vector> #include<queue> #include<cstdio> #include<algorithm> #include<cstring> using namespace std; void setIO(string a){ freopen((a+".in").c_str(),"r",stdin); } #define maxn 2000004 vector<int>G[maxn]; char arr[maxn],total[maxn]; int tag[maxn],ans[maxn],map[maxn],cnt=-1; struct Automaton{ #define idx str[i]-'a' #define root 0 #define sigma 27 int ch[maxn][sigma],last[maxn],fail[maxn],end[maxn]; int nodes; int newnode(){ return ++nodes;} void insert(char str[],int id){ int n=strlen(str); int pos=root; for(int i=0;i<n;++i){ if(!ch[pos][idx]) ch[pos][idx]=newnode(); pos=ch[pos][idx]; } G[pos].push_back(id); end[pos]=1; map[id]=pos; } queue<int>Q; void build(){ for(int i=0;i<sigma;++i) if(ch[root][i]) { Q.push(ch[root][i]),fail[ch[root][i]]=root; } while(!Q.empty()){ int u=Q.front(); Q.pop(); for(int i=0;i<sigma;++i){ int r=ch[u][i]; if(!r) { ch[u][i]=ch[fail[u]][i]; continue; } Q.push(r); fail[r]=ch[fail[u]][i]; last[r]=end[fail[r]]? fail[r]: last[fail[r]]; } } } bool vis[maxn]; void print(int j){ while(j){ if(end[j]) tag[j]+=1; j=last[j]; } } void query(char str[]){ int j=root; for(int i=0;i<cnt;++i){ if(str[i]=='#') j=root; else { j=ch[j][idx]; print(j); } } } }aho; int main(){ //setIO("input"); int n; scanf("%d",&n); for(int i=1;i<=n;++i) { scanf("%s",arr), aho.insert(arr,i); int cur_n=strlen(arr); for(int j=0;j<cur_n;++j) total[++cnt]=arr[j]; total[++cnt]='#'; ++cnt; } aho.build(); aho.query(total); for(int i=0;i<maxn;++i) for(int j=0;j<G[i].size();++j) ans[G[i][j]]+=tag[i]; for(int i=1;i<=n;++i) printf("%d\n",ans[i]); return 0; }