题目描述
小张最近在忙毕设,所以一直在读论文。一篇论文是由许多单词组成但小张发现一个单词会在论文中出现很多次,他想知道每个单词分别在论文中出现了多少次。
输入输出格式
输入格式:
第一行一个整数N,表示有N个单词。接下来N行每行一个单词,每个单词都由小写字母(a-z)组成。(N≤200)
输出格式:
输出N个整数,第i行的数表示第i个单词在文章中出现了多少次。
输入输出样例
输入样例#1:
3
a
aa
aaa
输出样例#1:
6
3
1
说明
数据范围
30%的数据, 单词总长度不超过10^3
100%的数据,单词总长度不超过10^6
题解:这一题太坑了,注意点有3个。
- 有重复出现的单词,所以要去重。
- 最后一个会TLE,所以重复的单词不要放到匹配字符串里面。
- 我是用string数组,为了构成一个匹配串,每输入一个单词s,str += 'z'+1, str += s;每个单词前面用一个‘z'+1,不仅起到分割的作用,还可以编号。跑AC自动机,每次跑到一个单词末尾,加上相应的个数
#include <bits/stdc++.h>
using namespace std;
int const N = 200 + 10; //单词的个数
int const M = 200 * 1000000 + 10;
struct Node{
Node *next[26];
Node *fail;
int num;
Node(){
memset(next,NULL,sizeof(next));
fail = NULL;
num = 0; //0表示这个模式串未出现,出现了用相应得编号代替;
}
}*p[M];
Node *root;
map<string,int>mp;
int n;
int val[N],mp2[N],cnt[N],cnt2[N];
void Insert(string s,int number){
Node *now = root;
for(int i=0;i<s.length();i++){
int to = s[i] - 'a';
if(now->next[to] == NULL)
now->next[to] = new Node();
now = now->next[to];
}
now->num = number;
}
void Get_Fail(){
int head = 0,tail = 0;
root->fail = NULL;
p[head++] = root;
while(head != tail){
Node *q = p[tail++];
Node *tmp;
for(int i=0;i<26;i++){
if(q->next[i] == NULL) continue;
if(q == root) q->next[i]->fail = root;
else{
tmp = q->fail;
while(tmp){
if(tmp->next[i]){
q->next[i]->fail = tmp->next[i];
break;
}
tmp = tmp->fail;
}
if(tmp == NULL) q->next[i]->fail = root;
}
p[head++] = q->next[i];
}
}
}
void Match(string s){
Node *tmp,*p = root;
int k = 0;
for(int i=0;i<s.length();i++){
int to = s[i] - 'a';
if(to == 'z'-'a'+1){
p = root;
k++; //表示第几个单词
continue;
}
while(p && p->next[to] == NULL) p = p->fail;
if(p) p = p->next[to];
else p = root;
tmp = p;
while(tmp != root){
if(tmp->num) val[tmp->num] += cnt[k];
tmp = tmp->fail;
}
}
}
void Print(){
for(int i=1;i<=n;i++){
cout<<val[mp2[i]]<<endl;
}
}
int main(){
ios::sync_with_stdio(false);
while(cin>>n&&n){
mp.clear();
memset(cnt,0,sizeof(cnt));
memset(cnt2,0,sizeof(cnt2));
memset(val,0,sizeof(val));
root = new Node();
string str;
int count = 0;
for(int i=1;i<=n;i++){
string s;
cin>>s;
if(!mp[s]){ //
Insert(s,++count);
str += 'z'+1, str += s; //为了防止TLE,每个单词只出现一次
mp[s] = mp2[i] = count; //mp2[i]表示每个单词的编号
cnt[count]++;//每个字符串的个数
}else cnt[mp2[i]=mp[s]]++; //记录这个单词出现了几次
}
Get_Fail();
Match(str);
Print();
}
return 0;
}
/*每个模式串给它一个编号*/