C. 单词
题目描述
原题来自:TJOI 2013
某人读论文,一篇论文是由许多单词组成。但他发现一个单词会在论文中出现很多次,现在想知道每个单词分别在论文中出现多少次。
输入格式
第一个一个整数 NNN,表示有多少个单词,接下来 NNN 行每行一个单词。
输出格式
输出 NNN 个整数,第 iii 行的数字表示第 iii 个单词在文章中出现了多少次。
样例
样例输入
3
a
aa
aaa
样例输出
6
3
1
数据范围与提示
对于全部数据,1≤N≤2001\le N\le 2001≤N≤200,所有单词长度的和不超过 10610^6106,保证每个单词由小写字母组成。
【题解】
本来以为是个板子题,然后发现并不是。开始居然没有理解题目……文章就是所有的单词合起来,中间加空格,但是空格很碍事,可以一个单词一个单词地匹配。
这个题让我重新思考了一下AC自动机……一开始我是通过ask操作通过调fail指针来更新count,然后T90,有一个比较恶心的点是每个字母的fail指针都是他的父亲节点,然后就死了。


#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct trie { int count; trie *next[26],*fail; trie() { count=0; fail=NULL; for(int i=0;i<26;i++) next[i]=NULL; } }*q[10000000],*root; int head,tail; trie *loc[210]; int n; char keyword[210][1000000]; void insert(char s[],trie *root,int t) { trie *p=root; int i=0,index; while(s[i]) { index=s[i]-'a'; if(p->next[index]==NULL)p->next[index]=new trie(); p=p->next[index]; i++; } loc[t]=p; } void build_ac(trie *root) { q[++tail]=root; while(head!=tail) { trie *p=q[++head]; trie *temp=NULL; for(int i=0;i<26;i++) if(p->next[i]!=NULL) { if(p==root)p->next[i]->fail=root; else { temp=p->fail; while(temp!=NULL) { if(temp->next[i]!=NULL) { p->next[i]->fail=temp->next[i]; break; } temp=temp->fail; } if(temp==NULL)p->next[i]->fail=root; } q[++tail]=p->next[i]; } } } void ask(trie *root,char str[]) { int i=0,index,cnt=0; trie *p=root; while(str[i]) { index=str[i]-'a'; while(p!=root && p->next[index]==NULL)p=p->fail; p=p->next[index]; if(p==NULL)p=root; trie *temp=p; while(temp!=root) { temp->count++; temp=temp->fail; } i++; } } signed main() { // freopen("in.txt","r",stdin); scanf("%d",&n); trie *root=new trie(); for(int i=1;i<=n;i++) { scanf("%s",keyword[i]); insert(keyword[i],root,i); } build_ac(root); for(int i=1;i<=n;i++) ask(root,keyword[i]); for(int i=1;i<=n;i++) printf("%d\n",loc[i]->count); }
其实可以在插入一个单词的时候对于他的路径上的点的count+1,表示这个前缀又出现了一次,根据AC自动机的性质,可以从下往上把i节点的count加到i->fail去。


#include<iostream> #include<cstdio> #include<cstring> using namespace std; struct trie { int count; trie *next[26],*fail; trie() { count=0; fail=NULL; for(int i=0;i<26;i++) next[i]=NULL; } }*q[10000000],*root; int head,tail; trie *loc[210]; int n; char keyword[210][1000000]; void insert(char s[],trie *root,int t) { trie *p=root; int i=0,index; while(s[i]) { p->count++; index=s[i]-'a'; if(p->next[index]==NULL){p->next[index]=new trie();} p=p->next[index]; i++; } p->count++; loc[t]=p; } void build_ac(trie *root) { q[++tail]=root; while(head!=tail) { trie *p=q[++head]; trie *temp=NULL; for(int i=0;i<26;i++) if(p->next[i]!=NULL) { if(p==root)p->next[i]->fail=root; else { temp=p->fail; while(temp!=NULL) { if(temp->next[i]!=NULL) { p->next[i]->fail=temp->next[i]; break; } temp=temp->fail; } if(temp==NULL)p->next[i]->fail=root; } q[++tail]=p->next[i]; } } } signed main() { // freopen("word10.in","r",stdin); scanf("%d",&n); trie *root=new trie(); for(int i=1;i<=n;i++) { scanf("%s",keyword[i]); insert(keyword[i],root,i); } build_ac(root); for(int i=tail;i>0;i--) { trie *temp=q[i]; if(temp->fail!=NULL)temp->fail->count+=temp->count; } for(int i=1;i<=n;i++) printf("%d\n",loc[i]->count); }