浅谈\(AC\)自动机:https://www.cnblogs.com/AKMer/p/10448651.html
题目传送门:https://www.luogu.org/problemnew/show/P3796
记录一下母串在\(AC\)自动机上面经过每个点的次数,然后深度较深的点用自己的次数累加在自己的\(fail\)上去,就能找出每个字符串出现的次数了。
时间复杂度:\(O(\sum len_i)\)
空间复杂度:\(O(\sum len_i)\)
代码如下:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=155,maxlen=1e6+5;
int n;
int ans[maxn];
char s[maxlen];
int read() {
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar())if(ch=='-')f=-1;
for(;ch>='0'&&ch<='9';ch=getchar())x=x*10+ch-'0';
return x*f;
}
struct data {
char s[75];
int len,node,id;
}a[maxn];
bool cmp(data a,data b) {
return a.id<b.id;
}
struct AC_automation {
int tot,root;
bool bo[maxn*70];
int son[maxn*70][26],list[maxn*70];
int fail[maxn*70],id[maxn*70],cnt[maxn*70];
void clear() {
tot=root=1;
memset(bo,0,sizeof(bo));
memset(id,0,sizeof(id));
memset(son,0,sizeof(son));
memset(cnt,0,sizeof(cnt));
memset(fail,0,sizeof(fail));
}
void ins(int ID) {
int pos=root;a[ID].id=ID;
a[ID].len=strlen(a[ID].s+1);
for(int i=1;i<=a[ID].len;i++) {
if(son[pos][a[ID].s[i]-'a'])
pos=son[pos][a[ID].s[i]-'a'];
else pos=son[pos][a[ID].s[i]-'a']=++tot;
}
id[pos]=ID,a[ID].node=pos;
}
void make_fail() {
int h=0,t=0;
for(int i=0;i<26;i++)
if(!son[root][i])son[root][i]=root;
else fail[son[root][i]]=root,list[t++]=son[root][i];
while(h!=t) {
int u=list[h++];
for(int i=0;i<26;i++)
if(son[u][i]) {
fail[son[u][i]]=son[fail[u]][i];
list[t++]=son[u][i];
}
else son[u][i]=son[fail[u]][i];
}
}
void find() {
int len=strlen(s+1),pos=root;
for(int i=1;i<=len;i++)
pos=son[pos][s[i]-'a'],cnt[pos]++;
int res=0;
for(int i=tot;i;i--)
if(!bo[i]) {
int pos=i,sum=0;
while(pos!=root) {
sum+=cnt[pos];
if(id[pos]) {
res=max(res,sum);
ans[id[pos]]=sum;
}bo[pos]=1;
pos=fail[pos];
}
}
printf("%d\n",res);
for(int i=1;i<=n;i++)
if(ans[i]==res)printf("%s\n",a[i].s+1);
}
}A;
int main() {
while(1) {
n=read();if(!n)break;A.clear();
for(int i=1;i<=n;i++)
scanf("%s",a[i].s+1),A.ins(i);
A.make_fail(),scanf("%s",s+1),A.find();
}
return 0;
}