題目背景
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;
}