题目链接
https://vjudge.net/problem/UVALive-4670
题意
有n个由小写字母组成的字符串和一个文本串T,你的任务是找出哪些字符串在文本中出现的次数最多。输出最多出现的次数,接下来每行包含一个出现次数最多的字符串,按照输入顺序排列。
解题
将n个字符串插入字典树,然后通过bfs寻找后缀结点构建ac自动机。
将文本串与ac自动机去匹配,统计每个字符出现的次数ans[i]。
然后遍历ans去找最多次数sum。用sum去与ans[i]比较,相等的话就输出第i个字符串。
一开始用了三个map去存,果断T了。后来注意到只需将尾结点编号与字符串编号做个映射就好了。
AC代码
#include <bits/stdc++.h>
using namespace std;
int sum;
int ans[200];
int num[155*70];
struct trie
{
int sz,ch[155*70][26],ed[155*70],fail[155*70];
void init()
{
sz=0;
memset(ch[0],0,sizeof(ch[0]));
memset(ans,0,sizeof(ans));
}
void insert(char *s,int id)
{
int len=strlen(s),p=0;
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
if(!ch[p][c])
{
ch[p][c]=++sz;
memset(ch[sz],0,sizeof(ch[sz]));
ed[sz]=0;
}
p=ch[p][c];
}
ed[p]++;
num[p]=id;
}
void build()
{
queue<int> que;
int p=0;
for(int i=0;i<26;i++)
{
int &c=ch[p][i];
if(c) fail[c]=0,que.push(c);
else c=0;
}
while(!que.empty())
{
int now=que.front();que.pop();
int nxt=fail[now];
for(int i=0;i<26;i++)
{
int &c=ch[now][i];
if(c) fail[c]=ch[nxt][i],que.push(c);
else c=ch[nxt][i];
}
}
}
void solve(char *s)
{
int len=strlen(s),p=0;
sum=0;
for(int i=0;i<len;i++)
{
int c=s[i]-'a';
p=ch[p][c];
int tmp=p;
while(tmp)
{
ans[num[tmp]]+=ed[tmp];
sum=max(sum,ans[num[tmp]]);
tmp=fail[tmp];
}
}
}
}ac;
const int maxn=1e6+7;
char str[maxn];
char P[200][80];
int main()
{
int n;
while(~scanf("%d",&n),n)
{
ac.init();
for(int i=1;i<=n;i++)
{
char s[80];
scanf("%s",P[i]);
ac.insert(P[i],i);
}
ac.build();
scanf("%s",str);
ac.solve(str);
printf("%d\n",sum);
for(int i=1;i<=n;i++)
{
if(ans[i]==sum) printf("%s\n",P[i]);
}
}
return 0;
}