【洛谷P3796】【模板】AC自动机(加强版)
题目
题目链接:https://www.luogu.com.cn/problem/P3796
有 \(N\) 个由小写字母组成的模式串以及一个文本串 \(T\)。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 \(T\) 中出现的次数最多。
思路
直接建立 AC 自动机,询问时暴力跳 fail 并记录每个串被走到的次数即可。
注意为了求出每个串出现次数就不用标记每个点是否被走过了。
时间复杂度 \(O(|S|\times |T|)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=1000010,M=80,K=160;
int n,ans,cnt[K];
char ch[K][M],s[N];
struct ACA
{
int tot,c[M*K][27],fail[M*K],pos[M*K];
void clr()
{
memset(c,0,sizeof(c));
memset(fail,0,sizeof(fail));
memset(pos,0,sizeof(pos));
tot=0;
}
void ins(char *s,int k)
{
int len=strlen(s+1),p=0;
for (int i=1;i<=len;i++)
{
int id=s[i]-'a'+1;
if (!c[p][id]) c[p][id]=++tot;
p=c[p][id];
}
pos[p]=k;
}
void build()
{
queue<int> q;
for (int i=1;i<=26;i++)
if (c[0][i]) q.push(c[0][i]);
while (q.size())
{
int u=q.front();
q.pop();
for (int i=1;i<=26;i++)
if (c[u][i]) fail[c[u][i]]=c[fail[u]][i],q.push(c[u][i]);
else c[u][i]=c[fail[u]][i];
}
}
void query(char *s)
{
int len=strlen(s+1),p=0;
for (int i=1;i<=len;i++)
{
int id=s[i]-'a'+1;
p=c[p][id];
for (int j=p;j;j=fail[j])
cnt[pos[j]]++;
}
}
}AC;
int main()
{
//freopen("data.txt","r",stdin);
while (scanf("%d",&n))
{
if (!n) return 0;
memset(cnt,0,sizeof(cnt));
AC.clr();
for (int i=1;i<=n;i++)
{
scanf("%s",ch[i]+1);
AC.ins(ch[i],i);
}
scanf("%s",s+1);
AC.build(); AC.query(s);
ans=0;
for (int i=1;i<=n;i++)
if (cnt[i]>ans) ans=cnt[i];
printf("%d\n",ans);
for (int i=1;i<=n;i++)
if (cnt[i]==ans) printf("%s\n",ch[i]+1);
}
}