【AC自动机】Lougu P3796
题目描述
有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
题解
写个AC自动机的板子上来。。。
反正就是先建一棵trie树,然后bfs找失配指针(类似KMP)。。。
然后再在上面搞些奇奇怪怪的东西。。。
代码
//by 减维 #include<cstdio> #include<iostream> #include<cstring> #include<queue> #include<cstdlib> #include<ctime> #include<cmath> #include<map> #include<bitset> #include<algorithm> #define ll long long #define maxn 1000005 using namespace std; struct trie{ int end,fail,to[26]; void cle(){ memset(to,0,sizeof(to)); end=fail=0; } }ac[maxn]; struct anss{ int pos,num; }ans[maxn]; int n,tot; string s[155]; bool cmp(const anss&x,const anss&y) { if(x.num==y.num)return x.pos<y.pos; return x.num>y.num; } void build(int x) { int now=0,ch; int len=s[x].length(); for(int i=0;i<len;++i) { ch=s[x][i]-'a'; if(!ac[now].to[ch])ac[now].to[ch]=++tot,ac[tot].cle(); now=ac[now].to[ch]; } ac[now].end=x; } void getf() { queue<int>q; for(int i=0;i<26;++i) if(ac[0].to[i])q.push(ac[0].to[i]),ac[ac[0].to[i]].fail=0; while(!q.empty()) { int d=q.front(); q.pop(); for(int i=0;i<26;++i) { int dd=ac[d].to[i]; if(dd)ac[dd].fail=ac[ac[d].fail].to[i],q.push(dd); else ac[d].to[i]=ac[ac[d].fail].to[i]; } } } void ask() { int now=0,ch; int len=s[0].length(); for(int i=0;i<len;++i) { ch=s[0][i]-'a'; now=ac[now].to[ch]; for(int j=now;j;j=ac[j].fail) if(ac[j].end)ans[ac[j].end].num++; } sort(ans+1,ans+1+n,cmp); printf("%d\n",ans[1].num); for(int i=1;i<=n;++i) if(ans[i].num==ans[1].num)cout<<s[ans[i].pos]<<endl; else break; } int main() { while(1) { scanf("%d",&n); if(n==0)return 0; tot=0;ac[0].cle(); for(int i=1;i<=n;++i) { cin>>s[i]; build(i); ans[i].pos=i; ans[i].num=0; } getf(); cin>>s[0]; ask(); } }