3172: [Tjoi2013]单词 fail树

首先建一棵Trie,我们考虑AC自动机中fail指针的含义。
与KMP中的fail指针相似,它指向的位置代表了一个相同前后缀。所以我们首先给每个点打标记,即每个前缀(root->此点)都出现了一次,然后在构造完fail指针后,我们会发现fail指针反向后是一棵树 沿着fail指针扫一遍其实就是沿着树边向根扫一遍。只在插入时将每个串的每个节点sum++ 那么每个串终点所在fail树的子树中sum的总和就是这个字符串的出现次数,只要根据BFS序求和就好了。

据说这题的作法很多?

#include<bits/stdc++.h>
using namespace std;
char s[1000005];
int n,cnt=1,pos[222],a[1000005][26],sum[1000005],p[1000005],q[1000005];
inline void insert(int &pos)
{
    scanf("%s",s);
    int l=strlen(s),x=1,c;
    for (int i=0;i<l;i++)
    {
        c=s[i]-'a';
        if (a[x][c]) x=a[x][c]; else x=a[x][c]=++cnt;
        sum[x]++;
    }
    pos=x;
}
inline void build_fail()
{
    int t=0,w=1,x;
    q[1]=1; p[1]=0;
    while (t<w)
    {
        x=q[++t];
        for (int i=0;i<26;i++)
            if (a[x][i])
            {
                int k=p[x];
                while (!a[k][i]) k=p[k];
                p[a[x][i]]=a[k][i];
                q[++w]=a[x][i];
            }
    }
    for (int i=w;i;i--) sum[p[q[i]]]+=sum[q[i]];
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<26;i++) a[0][i]=1;
    for (int i=1;i<=n;i++) insert(pos[i]);
    build_fail();
    for (int i=1;i<=n;i++) printf("%d\n",sum[pos[i]]);
    return 0;
}

你可能感兴趣的:(3172: [Tjoi2013]单词 fail树)