题意:给出n个单词和一个长字符串,找出在长字符串里出现次数最多的单词,输出出现次数和单词。如果多个单词出现次数同为最多,按输入顺序输出单词。
思路:AC自动机。题意非常明显,应该算是AC自动机的模板题。我的理解是,AC自动机就是字典树和KMP算法的结合,如果匹配失败了,节点就在树上跳转,跳转到可能匹配的深度最大的节点(当然与原节点相比深度会减小)。这题用AC自动机匹配并统计,然后扫一遍输入的单词,符合条件的输出即可。
#include <iostream>
#include <stdio.h>
#include <cmath>
#include <algorithm>
#include <iomanip>
#include <cstdlib>
#include <string>
#include <memory.h>
#include <vector>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <ctype.h>
#define INF 1<<30
#define ll long long
#define max3(a,b,c) max(a,max(b,c))
#define MAXN 4100000
using namespace std;
char text[1000010];
int ans;
char word[160][80];
int cnt[160];
struct Trie{
int ch[10510][26];
char c[10510];
int val[10510];
int sz;
int next[10510];
int pre[10510];
void init(){
sz=1;
memset(ch,0,sizeof(ch));
memset(val,0,sizeof(val));
memset(cnt,0,sizeof(cnt));
}
void insert(char* str,int w){
int u=0;
int len=strlen(str);
for(int i=0;i<len;i++){
if( !ch[u][str[i]-'a'] ){
memset(ch[sz],0,sizeof(ch[sz]));
c[sz]=str[i];
ch[u][str[i]-'a']=sz;
pre[sz]=u;
sz++;
}
u=ch[u][str[i]-'a'];
}
val[u]=w;
}
void build_ac(){
queue<int> que; que.push(0);
while(!que.empty()){
int u=que.front(); que.pop();
if(!u){
for(int i=0;i<26;i++){
int v=ch[u][i];
if( !v )continue;
next[v]=0;
que.push(v);
}
}else{
for(int i=0;i<26;i++){
int v=ch[u][i];
if(!v)continue;
int k=next[u];
while(k&&ch[k][i]==0)k=next[k];
if(ch[k][i])k=ch[k][i];
next[v]=k;
que.push(v);
}
}
}
}
void query(char* text){
int u=0;
int len=strlen(text);
for(int i=0;i<len;i++){
int v=ch[u][text[i]-'a'];
if(v){
u=v;
//
while(val[u]){
//cout<<"match at "<<i<<endl;
cnt[val[u]]++;
if(cnt[val[u]]>ans)ans=cnt[val[u]];
u=next[u];
}
while(u&&!ch[u][text[i+1]-'a'])u=next[u];
}else{
while(u&&!ch[u][text[i+1]-'a'])u=next[u];
}
}
}
};
Trie T;
int main(){
int n;
while(cin>>n){
if(!n)break;
T.init();
ans=0;
for(int i=1;i<=n;i++){
scanf("%s",word[i]);
T.insert(word[i],i);
}
scanf("%s",text);
T.build_ac();
T.query(text);
cout<<ans<<endl;
for(int i=1;i<=n;i++){
if(cnt[i]==ans){
printf("%s\n",word[i]);
}
}
}
}