题目描述
有 NNN 个由小写字母组成的模式串以及一个文本串 TTT 。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串 TTT 中出现的次数最多。
输入输出格式
输入格式:输入含多组数据。
每组数据的第一行为一个正整数 NNN ,表示共有 NNN 个模式串, 1≤N≤1501 leq N leq 1501≤N≤150 。
接下去 NNN 行,每行一个长度小于等于 707070 的模式串。下一行是一个长度小于等于 10610^6106 的文本串 TTT 。
输入结束标志为 N=0N=0N=0 。
输出格式:对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。
输入输出样例
输入样例#1:
2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0
输出样例#1:
4
aba
2
alpha
haha
Solution:
额,还是比较板子的AC自动机。
略过AC自动机的构建(本题最好搞个后缀链接的优化),我们只需要在匹配的时候,记录一下各字符串出现的次数,最后比较一下就好了,坑点是除了字符串其它所有数组都要清0(trie,end,fail,last,cnt,num)。
其实本题原题蓝书上有,那里会有重复的模式串,而本题没有,重复的模式串会在trie树中覆盖掉前面出现的字符串的end,可以思考一下加加强版,实现方法很多。
代码:
#include<bits/stdc++.h> #define il inline #define ll long long #define For(i,a,b) for(int (i)=(a);(i)<=(b);(i)++) #define Bor(i,a,b) for(int (i)=(b);(i)>=(a);(i)--) using namespace std; const int N=10305,M=1000005; int trie[N][26],cnt,end[N],n,num[155],last[N],fail[N]; char s[155][72],t[M]; il void cler(){ memset(trie,0,sizeof(trie)); memset(num,0,sizeof(num)); memset(end,0,sizeof(end)); memset(fail,0,sizeof(fail)); memset(last,0,sizeof(last)); cnt=0; } il void insert(char *s,int id){ int len=strlen(s)-1,p=0,x; For(i,0,len){ x=s[i]-'a'; if(!trie[p][x])trie[p][x]=++cnt; p=trie[p][x]; } end[p]=id; } il void bfs(){ queue<int>q; For(i,0,25) if(trie[0][i]) fail[trie[0][i]]=0,q.push(trie[0][i]); while(!q.empty()){ int u=q.front();q.pop(); For(i,0,25){ int v=trie[u][i]; if(v) fail[v]=trie[fail[u]][i],last[v]=end[fail[v]]?fail[v]:last[fail[v]],q.push(v); else trie[u][i]=trie[fail[u]][i]; } } } il void query(char *t){ int len=strlen(t)-1,p=0,ans=0; For(i,0,len){ p=trie[p][t[i]-'a']; for(int j=p;j;j=last[j]) if(end[j]) num[end[j]]++; } For(i,1,n) ans=max(num[i],ans); printf("%d ",ans); For(i,1,n) if(num[i]==ans) puts(s[i]); } int main(){ while(scanf("%d",&n)==1){ if(!n) exit(0); cler(); For(i,1,n) scanf("%s",s[i]),insert(s[i],i); bfs(); scanf("%s",t); query(t); } return 0; }