【模板】AC自动机(加强版)
题目描述
有NN 个由小写字母组成的模式串以及一个文本串TT 。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串TT 中出现的次数最多。
输入输出格式
输入格式:
输入含多组数据。
每组数据的第一行为一个正整数NN ,表示共有NN 个模式串,1 \leq N \leq 1501≤N≤150 。
接下去NN 行,每行一个长度小于等于7070 的模式串。下一行是一个长度小于等于10^610
6
的文本串TT 。
输入结束标志为N=0N=0 。
输出格式:
对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。
输入输出样例
输入样例#1:
2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0
输出样例#1:
4
aba
2
alpha
haha
分析:fail指针说一下,ac[ac[u].nxt[i]].fail=ac[ac[u].fail].nxt[i]。怕以后忘了。。。
代码
#include <cstdio>
#include <string>
#include <cstring>
#include <iostream>
#include <queue>
#define N 100000
using namespace std;
queue <int>q;
struct node
{
int nxt[26],en,fail,sce;
}trie[N];
int n,ans,num,a[160];
string s[160];
void ins(string s1,int x)
{
int len=s1.length();
int u=0;
for (int i=0;i<len;i++)
{
int v=s1[i]-'a';
if (!trie[u].nxt[v]) trie[u].nxt[v]=++num;
u=trie[u].nxt[v];
}
trie[u].en=1;
trie[u].sce=x;
}
void getfail()
{
while (!q.empty()) q.pop();
for (int i=0;i<26;i++)
if (trie[0].nxt[i])
{
trie[trie[0].nxt[i]].fail=0;
q.push(trie[0].nxt[i]);
}
while (!q.empty())
{
int u=q.front();
q.pop();
for (int i=0;i<26;i++)
if (trie[u].nxt[i])
{
trie[trie[u].nxt[i]].fail=trie[trie[u].fail].nxt[i];
q.push(trie[u].nxt[i]);
}
else trie[u].nxt[i]=trie[trie[u].fail].nxt[i];
}
}
void find(char *s1)
{
int len=strlen(s1);
int u=0;
for (int i=0;i<len;i++)
{
int v=s1[i]-'a';
u=trie[u].nxt[v];
for (int j=u;j;j=trie[j].fail)
if (trie[j].en) a[trie[j].sce]++;
}
}
int main()
{
scanf("%d",&n);
while (n)
{
if (n==0) break;
num=0;ans=0;
memset(trie,0,sizeof(trie));
memset(a,0,sizeof a);
for (int i=1;i<=n;i++)
{
cin>>s[i];
ins(s[i],i);
}
getfail();
char t[1000005];
scanf("%s",t);
find(t);
for (int i=1;i<=n;i++)
ans=max(ans,a[i]);
printf("%d\n",ans);
for (int i=1;i<=n;i++)
if (a[i]==ans) cout<<s[i]<<endl;
scanf("%d",&n);
}
}