您的位置:首页 > 其它

P3796 【模板】AC自动机(加强版)

2017-07-06 17:11 337 查看

题目描述



个由小写字母组成的模式串以及一个文本串

。每个模式串可能会在文本串中出现多次。你需要找出哪些模式串在文本串

中出现的次数最多。

输入输出格式

输入格式:

输入含多组数据。

每组数据的第一行为一个正整数

,表示共有

个模式串,



接下去

行,每行一个长度小于等于

的模式串。下一行是一个长度小于等于

的文本串



输入结束标志为



输出格式:

对于每组数据,第一行输出模式串最多出现的次数,接下去若干行每行输出一个出现次数最多的模式串,按输入顺序排列。

输入输出样例

输入样例#1:

2
aba
bab
ababababac
6
beta
alpha
haha
delta
dede
tata
dedeltalphahahahototatalpha
0


输出样例#1:

4
aba
2
alpha
haha


就是个纯模板,没啥好说的,。

不过AC自动机真的是,,恶心到爆,,,

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#include<algorithm>
#include<map>
using namespace std;
const int MAXN=100001;
void read(int &n)
{
char c='+';int x=0;bool flag=0;
while(c<'0'||c>'9'){c=getchar();if(c=='-')flag=1;}
while(c>='0'&&c<='9'){x=x*10+c-48;c=getchar();}
flag==1?n=-x:n=x;
}
char s[MAXN][101];
char text[1000001];
struct AC_Automata
{
int sz;
int ch[MAXN][28];//trie树
int cnt[MAXN];// 每个单词出现的次数
int val[MAXN];// 记录是否有单词
int last[MAXN];
int f[MAXN];
int sigma_sz;
map<string,int>mp;//出现的位置
void Init()
{
memset(ch[0],0,sizeof(ch[0]));
memset(cnt,0,sizeof(cnt));
mp.clear();
sz=1;
sigma_sz=26;
}
int idx(char c)
{
return c-'a';
}
void Insert(char *s,int pos)
{
int l=strlen(s); int now=0;
for(int i=0;i<l;i++)
{
int c=idx(s[i]);
if(!ch[now][c])
{
memset(ch[sz],0,sizeof(ch[sz]));
val[sz]=0;
ch[now][c]=sz++;
}
now=ch[now][c];
}
val[now]=pos;//一个单词的结束
mp[string(s)]=pos; // 记录这个字符串出现的位置,按顺序输出
}
void getfail()
{
f[0]=0;
queue<int>q;
for(int i=0;i<sigma_sz;i++)
{
int u=ch[0][i];
if(u)// 此处有单词出现
{
f[u]=0;// 连向根节点
q.push(u);
last[u]=0;// 上一个出现的单词一定是根节点
// 上面两条语句没什么卵用,只是为了帮助理解AC自动机的含义
}
}
while(!q.empty())
{
int p=q.front();q.pop();
for(int i=0;i<sigma_sz;i++)
{
int u=ch[p][i];
if(!u)//没有孩子
{
ch[p][i]=ch[f[p]][i];// 补上一条不存在的边,减少查找次数
continue;
}
q.push(u);
int v=f[p];// 找到它的失陪指针
f[u]=ch[v][i];//因为我们在上面补上了一条不存在边,所以他一定存在孩子
last[u]=val[f[u]] ? f[u] : last[f[u]];
//判断下是不是单词结尾  是, 不是
}
}
}
int ok(int pos)
{
if(pos)
{
cnt[val[pos]]++;
ok(last[pos]);
}
}
void find(char *s)// 查找模式串
{
int l=strlen(s);
int j=0;// 当前节点的编号
for(int i=0;i<l;i++)
{
int c=idx(s[i]);
while(j&&!ch[j][c])
j=f[j];// 顺着失配边走
j=ch[j][c]; // 取到儿子
if(val[j])
ok(j);// 找到一个单词
else if(last[j])
ok(last[j]);// 当前节点不行就看看上一个单词出现的位置行不行
}
}
}ac;
int n;
int main()
{
while(scanf("%d",&n)==1&&n!=0)
{
ac.Init();
for(int i=1;i<=n;i++)
{
scanf("%s",s[i]);
ac.Insert(s[i],i);
}
ac.getfail();
scanf("%s",text);
ac.find(text);
int ans=-1;
for(int i=1;i<=n;i++)
if(ac.cnt[i]>ans) ans=ac.cnt[i];
printf("%d\n",ans);
for(int i=1;i<=n;i++)
if(ac.cnt[i]==ans)
printf("%s\n",s[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: