解题思路
我笑了,加强版和减弱版基本毫无区别。。。
》》AC自动机板子
首先题目统计的是出现次数最多的字符串,所以有重复的字符串是没有关系的。
那么我们就将标记模式串的flag设为当前是第几个模式串。就是下面插入时的变化:
trie[p].cnt++;
变为
trie[p].v=num; //num表示该字符串是第num个输入的
求Fail指针没有变化,原先怎么求就怎么求。
查询:我们开一个数组 v i s vis vis,表示第i个字符串出现的次数。因为是重复计算,所以不能标记为 − 1 -1 −1了。
我们每经过一个点,如果有模式串标记,就将 v i s [ 模 式 串 标 记 ] + + vis[模式串标记]++ vis[模式串标记]++。然后继续跳 f a i l fail fail。
输出最大的 v i s vis vis,即 v i s vis vis等于最大的 v i s vis vis的串。
代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll long long
#define ldb long double
using namespace std;
int n,tot,T,ans,vis[200];
char ss[1001000],s[200][100];
struct c{
int fail,v;
int son[30];
}trie[1001000];
void insert(char s[],int i){
int len=strlen(s),p=0;
for(int i=0;i<len;i++)
{
char c=s[i]-'a';
if(!trie[p].son[c])
trie[p].son[c]=++tot;
p=trie[p].son[c];
}
trie[p].v=i;
}
void getfail(){
queue<int>q;
for(int i=0;i<26;i++)
{
int c=trie[0].son[i];
if(c)
{
trie[c].fail=0;
q.push(c);
}
}
while(!q.empty())
{
int x=q.front();
q.pop();
int f=trie[x].fail;
for(int i=0;i<26;i++)
{
int c=trie[x].son[i];
if(c)
{
trie[c].fail=trie[f].son[i];
q.push(c);
}
else trie[x].son[i]=trie[f].son[i];
}
}
}
void find(char s[]){
int x=0,sum=0,len;
len=strlen(s+1);
for(int i=1;i<=len;i++)
{
int v=s[i]-'a';
int c=trie[x].son[v];
while(c>0)
{
if(trie[c].v)
vis[trie[c].v]++;
c=trie[c].fail;
}
x=trie[x].son[v];
}
}
int main(){
while(scanf("%d",&n))
{
if(!n)break;
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]);
insert(s[i],i);
}
getfail();
scanf("%s",ss+1);
find(ss);
for(int i=1;i<=n;i++)
ans=max(ans,vis[i]);
printf("%d\n",ans);
for(int i=1;i<=n;i++)
if(vis[i]==ans)
printf("%s\n",s[i]);
ans=0,tot=0;
memset(vis,0,sizeof(vis));
memset(trie,0,sizeof(trie));
}
}