题目大意:给出n个单词,输出每个单词在所有单词中出现的次数。
fail树真的是神奇…这里讲的还不错,可以看看(原来我写的AC自动机是Trie图)
Fail树的一个性质是,某个结点对应的字符串肯定是其子树结点对应的字符串的后缀。
这道题想找出每一个串在所有的串中匹配的次数,这时就可以利用fail树的这个性质,而且fail树也是建立在Trie上的,统计子树中所有结点个数,就代表其在所有串中的前缀中作为后缀出现的次数,即在所有串中的出现次数。在队列中越靠后的点就越靠近叶子,用队列倒着更新即可。
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;
struct Node {
Node *ch[26],*nxt;
int val;
Node():nxt(0x0),val(0){memset(ch,0,sizeof ch);}
}*root=new Node(),*p[205];
char s[1000005];
Node* Insert() {
int len=strlen(s);
Node* o=root;
for(int i=0;i<len;i++) {
o->val++;
int z=s[i]-'a';
if(!o->ch[z]) o->ch[z]=new Node();
o=o->ch[z];
}
o->val++;
return o;
}
void getFail() {
static Node* q[1000005];
int l,r; l=r=0;
Node* o=root;
for(int i=0;i<26;i++)
if(o->ch[i]) q[r++]=(o->ch[i]) , o->ch[i]->nxt=o;
else o->ch[i]=o;
while(l<r) {
o=q[l++];
for(int i=0;i<26;i++)
if(o->ch[i]) q[r++]=(o->ch[i]) , o->ch[i]->nxt=o->nxt->ch[i];
else o->ch[i]=o->nxt->ch[i];
}
for(int i=r-1;~i;i--)
q[i]->nxt->val+=q[i]->val;
return ;
}
int main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%s",s) , p[i]=Insert();
getFail();
for(int i=1;i<=n;i++) printf("%d\n",p[i]->val);
return 0;
}