题意:给定n个单词(均为大写字母)和一个文本(均为可见字符),求每个单词在文本中出现的次数。单词数不超过1000,长度不超过50,文本长度不超过2000000
分析:AC自动机基础题。
一个小的优化:由于单词均为大写字母,所以建字典树时第二维大小可以只开26,这样可以节约时间和空间,在扫描文本时(均为可见字符),碰到非大写字母时,直接将指针指向根结点,继续扫描下一个字符即可。
View Code
#include <stdio.h> #include <string.h> #include <queue> using namespace std; #define LEN 51 #define N 1001 char str[N][LEN]; char t[2000001]; int n,node; int next[N*LEN][26]; int fail[N*LEN]; int id[N*LEN]; int ans[N]; void init() { node=1; memset(next[0],0,sizeof(next[0])); memset(ans,0,sizeof(ans)); } void add(int cur,int k) { memset(next[node],0,sizeof(next[node])); id[node]=0; next[cur][k]=node++; } void insert(int k,char s[]) { int i,j,cur=0; for(i=0;s[i];i++) { j=s[i]-'A'; if(!next[cur][j]) add(cur,j); cur=next[cur][j]; } id[cur]=k; } void CalFail() { int i,k,cur,nxt,tmp; queue<int>q; fail[0]=0; q.push(0); while(!q.empty()) { cur=q.front(),q.pop(); for(i=0;i<26;i++) { nxt=next[cur][i]; if(!nxt) continue; if(!cur) fail[nxt]=0; else { for(tmp=fail[cur];tmp && !next[tmp][i];tmp=fail[tmp]); fail[nxt]=next[tmp][i]; } q.push(nxt); } } } void match(char s[]) { int i,k,cur=0,tmp; for(i=0;s[i];i++) { k=s[i]-'A'; if(k<0 || k>=26) {cur=0;continue;} for(;cur && !next[cur][k];cur=fail[cur]); cur=next[cur][k]; for(tmp=cur;tmp;tmp=fail[tmp]) { ans[id[tmp]]++; } } } int main() { int i; while(~scanf("%d",&n)) { init(); for(i=1;i<=n;i++) { scanf("%s",str[i]); insert(i,str[i]); } scanf("%s",t); CalFail(); match(t); for(i=1;i<=n;i++) { if(ans[i]) printf("%s: %d\n",str[i],ans[i]); } } return 0; }