题目大意及后缀数组做法见 http://blog.csdn.net/popoqqq/article/details/41042473
原来正解是fail树……难怪后缀数组被卡成这样
首先我们将给出的n个串构建AC自动机
朴素的做法是对于每个串将这个串每个节点沿着fail指针扫一遍,将路径上的所有点的cnt++
但是这样做会TLE
我们不妨反向思考 fail指针反向后是一棵树 沿着fail指针扫一遍就是沿着树边向根扫一遍
只在插入时将每个串的每个节点cnt++ 那么每个串终点所在fail树的子树中cnt的总和就是这个字符串的出现次数
于是每个点记录所在fail树的子树中cnt的数量 可以线性时间内出解
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 1001001 using namespace std; struct Trie{ Trie *son[26],*fail; int cnt; }*root,mempool[M],*C=mempool; int n; Trie *pos[210]; char s[M]; void Insert(Trie* &p,char *s,Trie* &pos) { if(!p) p=C++; p->cnt++; if(!*s) { pos=p; return ; } Insert(p->son[(*s)-'a'],s+1,pos); } void Build_Tree() { static Trie *q[M]; static int r,h; int i; for(i=0;i<26;i++) if(root->son[i]) root->son[i]->fail=root,q[++r]=root->son[i]; while(r!=h) { Trie *p=q[++h]; for(i=0;i<26;i++) if(p->son[i]) { Trie *temp=p->fail; while(temp!=root&&!temp->son[i]) temp=temp->fail; if(temp->son[i]) temp=temp->son[i]; p->son[i]->fail=temp; q[++r]=p->son[i]; } } for(i=r;i;i--) q[i]->fail->cnt+=q[i]->cnt; } int main() { int i; cin>>n; for(i=1;i<=n;i++) scanf("%s",s),Insert(root,s,pos[i]); Build_Tree(); for(i=1;i<=n;i++) printf("%d\n",pos[i]->cnt); return 0; }
原文地址:http://blog.csdn.net/popoqqq/article/details/41808305