BZOJ3172 [Tjoi2013]单词(洛谷P3966)

AC自动机

BZOJ题目传送门
洛谷题目传送门

不得不说fail树太神奇了。

每次插入一个单词时把所有路径上的点都+1,每个单词的出现次数就是其结尾节点的fail树子树和。因为其fail指针指向的节点一定包含当前字符串。

统计贡献时要从下往上加,所以最好BFS的时候保存其BFS序后倒着搞。

代码:

#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 1000005
using namespace std;
int n,nd,t[N][26],nxt[N],f[N],id[N],p[N];
char s[N];
queue <int> q;
inline void nsrt(int id){
    int x=0;
    for (int i=0;s[i];i++){
        int ch=s[i]-'a';
        if (!t[x][ch]) t[x][ch]=++nd;
        x=t[x][ch],f[x]++;
    }
    p[id]=x;
}
inline void build(){
    while (!q.empty()) q.pop(); nd=0;
    for (int i=0,v;i<26;i++)
        if (v=t[0][i]) q.push(v),id[v]=++nd;
    while (!q.empty()){
        int x=q.front(),fa=nxt[x]; q.pop();
        for (int i=0,v;i<26;i++)
            if (v=t[x][i])
                q.push(v),id[v]=++nd,nxt[v]=t[fa][i];
            else t[x][i]=t[fa][i];
    }
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%s",s),nsrt(i);
    build();
    for (int i=nd;i;i--) f[nxt[id[i]]]+=f[id[i]];
    for (int i=1;i<=n;i++) printf("%d\n",f[p[i]]);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a1799342217/article/details/81086598