题目描述
题解
记录每一个点建自动机时候的访问次数。
建Fail树,然后节点子树的大小即为当前点出现的次数。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
using namespace std;
const int max_n=2e2+5;
const int max_s=1e6+5;
int n,pos[max_n];
int tot,ans[max_s],query[max_s],cnt;
char s[max_s];
int ch[max_s][30],fail[max_s];
queue <int> q;
inline void insert(int &loc){
int now=0,len=strlen(s);
for (int i=0;i<len;++i){
int x=s[i]-'a';
if (!ch[now][x])
ch[now][x]=++tot;
now=ch[now][x];
++ans[now];
}
loc=now;
}
inline void make_fail(){
while (!q.empty()) q.pop();
for (int i=0;i<26;++i)
if (ch[0][i])
q.push(ch[0][i]);
while (!q.empty()){
int now=q.front(); q.pop();
query[++cnt]=now;
for (int i=0;i<26;++i){
if (!ch[now][i]){
ch[now][i]=ch[fail[now]][i];
continue;
}
fail[ch[now][i]]=ch[fail[now]][i];
q.push(ch[now][i]);
}
}
}
inline void calc_ans(){
for (int i=cnt;i>=1;--i)
ans[fail[query[i]]]+=ans[query[i]];
}
int main(){
scanf("%d\n",&n);
for (int i=1;i<=n;++i){
gets(s);
insert(pos[i]);
}
make_fail();
calc_ans();
for (int i=1;i<=n;++i)
printf("%d\n",ans[pos[i]]);
}
总结
Fail神奇的特性:所有以x节点代表的字符串结尾的字符串节点,都在x的子树中。