P3796 【模板】AC自动机
AC自动机的模板
简单的理解就是字典树上的KMP
注意数组不要开太大
不然每次memset耗时太多
有一个小优化
每次走 fail 边找匹配时只有一些会更新答案
那么就可以把没用的fail边压缩掉
设 g[x] 表示从 x 点一直走 fail 边,走到的第一个有结束标记的点
那么找匹配时就只有要 g 边
然后就是模板了
#include<iostream> #include<cstring> #include<cstdio> #include<cmath> #include<algorithm> #include<queue> using namespace std; const int N=5e4+7; int len,n; char s[1000007]; inline void read_s()//文本串比较大,可以用快读加速 { char ch=getchar(); len=0; while(ch<'a'||ch>'z') ch=getchar(); while(ch>='a'&&ch<='z') { s[++len]=ch; ch=getchar(); } } int c[N][27],pd[N],fail[N],g[N],cnt; char a[1670][670]; int l[570]; inline void clr()//清空AC自动机 { memset(c,0,sizeof(c)); memset(pd,0,sizeof(pd)); memset(fail,0,sizeof(fail)); memset(g,0,sizeof(g)); cnt=0; } inline void ins(int num)//插入单词到自动机(跟字典树没区别) { int u=0; for(int i=0;i<l[num];i++) { int v=a[num][i]-'a'+1; if(!c[u][v]) c[u][v]=++cnt; u=c[u][v]; } pd[u]=num; } queue <int> q; inline void pre()//预处理fail和g { for(int i=1;i<=26;i++) if(c[0][i]) q.push(c[0][i]); while(!q.empty()) { int u=q.front(); q.pop(); for(int i=1;i<=26;i++) { int v=c[u][i]; if(!v) c[u][i]=c[fail[u]][i];//失配了直接走失配边 else { fail[v]=c[fail[u]][i]; g[v]= pd[fail[v]] ? fail[v] : g[fail[v]];//处理g q.push(v); } } } } int mx,ans[167]; inline void query()//AC自动机匹配(好像跟字典树也没什么区别) { memset(ans,0,sizeof(ans)); int u=0; for(int i=1;i<=len;i++) { u=c[u][s[i]-'a'+1]; if(pd[u]) ans[pd[u]]++; for(int j=g[u];j;j=g[j]) ans[pd[j]]++; //计算匹配 } mx=0; for(int i=1;i<=n;i++) mx=max(mx,ans[i]); cout<<mx<<endl; for(int i=1;i<=n;i++) if(ans[i]==mx) printf("%s\n",a[i]); } int main() { while(1) { scanf("%d",&n); if(!n) break; clr(); for(int i=1;i<=n;i++) { scanf("%s",a[i]); l[i]=strlen(a[i]); ins(i); } pre(); read_s(); query(); } return 0; }