分析:本题模板串多且长度短,文本串却很长,适合使用AC自动机。
一个容易忽略的地方是重复出现的模板,如果有模板重复,后一个子串会覆盖前一个子串。因此要建立一个字符串到编号的索引map<string,int>mp,每次初始化时候清空。
代码如下:
#include <cstring>
#include <cstdio>
#include <queue>
#include <map>
#include <string>
using namespace std;
const int sigma_size = 26;
const int maxs = 150+10;
const int maxn = 11000;
map<string,int>mp;
queue<int>que;
int ch[maxn][sigma_size];
int fail[maxn]; //fail函数
int val[maxn]; //每个字符串的结尾结点都有一个非0的val
int last[maxn]; //输出链表的下一个结点
int cnt[maxs];
char p[maxs][80];
char text[1000000+10];
int sz;
int N;
int idx(char c) { return c-'a';}
//插入字符串,v必须非零
void Insert(char *s, int v){
int u = 0, len = strlen(s);
int c;
for (int i=0; i<len; i++){
c = idx(s[i]);
if (!ch[u][c]) {
memset(ch[sz],0,sizeof(ch[sz]));
val[sz] = 0;
ch[u][c] = sz++;
}
u = ch[u][c];
}
val[u] = v;
mp[string(s)] = v;
}
void init(){
sz = 1;
memset(ch[0],0,sizeof(ch[0]));
memset(cnt,0,sizeof(cnt));
mp.clear();
for (int i=1; i<=N; i++) {
scanf("%s",p[i]);
Insert(p[i],i);
}
}
void getfail(){
while(!que.empty()) que.pop();
fail[0] = 0;
int u,r,v;
for (int i=0; i<sigma_size; i++) {
u = ch[0][i];
if (u) {fail[u] = 0; que.push(u); last[u] = 0;}
}
while (!que.empty()){
r = que.front(); que.pop();
for (int c=0; c<sigma_size; c++){
u = ch[r][c];
if (!u) continue;
que.push(u);
v = fail[r];
while (v && !ch[v][c]) v = fail[v];
fail[u] = ch[v][c];
last[u] = val[fail[u]]? fail[u]:last[fail[u]];
}
}
}
void Print(int x){
if (x){
cnt[val[x]]++;
Print(last[x]);
}
}
void Find(char *T){
int len = strlen(T);
int j = 0,c;
for (int i=0; i<len; i++){
c = idx(T[i]);
while (j && !ch[j][c]) j = fail[j];
j = ch[j][c];
if (val[j]) Print(j);
else if (last[j]) Print(last[j]);
}
}
int MAX(int x, int y){return x>y?x:y;}
int main(){
while (scanf("%d",&N) && N){
init();
getfail();
scanf("%s",text);
Find(text);
int best = -1;
for (int i=1; i<=N; i++) best = MAX(best,cnt[i]);
printf("%d\n",best);
for (int i=1; i<=N; i++) if (best==cnt[mp[string(p[i])]]) printf("%s\n",p[i]);
}
return 0;
}