您的位置:首页 > 其它

TJOI 2013 单词(AC自动机)

2017-07-14 10:39 99 查看
题目背景

TJOI2013 DAY1 T3

题目描述

小张最近在忙毕业论文设计,所以一直在读论文。一篇论文是由许多单词组成的。

但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现多少次。

输入格式

第一行一个整数 N (N≤200),表示有 N 个单词。接下来 N 行每行一个单词。每个单词都由小写字母(’a’~’z’)组成。

所有单词构成论文(一行一个单词)。

输出格式

输出 N 个整数,第 i 行的数字表示第 i 个单词在文章中出现了多少次。

样例数据 1

输入

3

a

aa

aaa

输出

6

3

1

备注

【数据范围】

30%„ 的数据,单词总长度不超过 10^3 ;

100% 的数据,单词总长度不超过 10^6 。

读完题后我们不难发现这是一道AC自动机的题,如果不会AC自动机请参考AC自动机算法AC自动机模板

假设现在大家都会AC自动机:

首先我们需要把所有的单词都存到一个字符数组里来表示文章,每个单词中间可以用一些小符号隔开来表示不同的单词,例如 ‘a’+26,输出为 <;

然后我们就构建tri树,和fail指针;

然后直接匹配,具体过程见代码

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<iomanip>
#include<iostream>
#include<cctype>
using namespace std;
//---------------------
char s[1000050],s1[1000050];
int now,pos,n,l,len,f[1000050][27],tot,tag[1000050];
int fail[1000050],next[1000050],nt,first[1000050],ans[1000050];
int head=0,tail=1,q[1000050];
//---------------------
inline void f1() //构建tri树
{
int t=0;
for(int i=1;i<=l;i++)
{  if(!f[t][s[i]-'a'])
f[t][s[i]-'a']=++tot;
t=f[t][s[i]-'a'];
}
}
//---------------------
inline void ac()
{
int j,i;
q[1]=0;
while(head^tail)
{
head++;
j=q[head];
if(j)
for(i=0;i<=25;i++)
if(f[j][i])
fail[f[j][i]]=f[fail[j]][i];
for(i=0;i<26;i++)
if(!f[j][i])
f[j][i]=f[fail[j]][i];
else q[++tail]=f[j][i];
}
}
//---------------------
int main()
{
//freopen("word.in","r",stdin);

scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%s",s+1);
l=strlen(s+1);
f1();
s1[++len]='a'+26;
for(int i=1;i<=l;i++)
s1[++len]=s[i];
}
ac();   //构建fail指针
for(;pos<len;pos++)  tag[now=f[now][s1[pos+1]-'a']]++; //扫一遍统计每个字母在文章中出现的次数
for(int i=tail;i;i--) tag[fail[q[i]]]+=tag[q[i]]; //因为当前的字符会在它的fail指针处再出现,所以需要累加,例如样例中的a 出现6次
bool asdf=0; //判断是否为第一个空格
int t=0;
for(int i=1;i<=len;i++){ //扫描匹配输出答案
if(s1[i]=='a'+26){
if(!asdf) asdf=1;//第一个空格不能输出,因为前面是没有单词的
else cout<<tag[t]<<endl;
t=0;
}
else t=f[t][s1[i]-'a'];
}
cout<<tag[t]; //最后一个单词
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: