bzoj 3172单词 (AC自动机fail树)

传送门

题意

给出$N$个单词,求每个单词在所有单词中出现的次数之和。

题解

  • 对$n$个单词建出$AC$自动机,求出$fail$指针后再将$fail$指针反向连边就得到了一颗以$0$为根的树。
  • $fail$树有一个性质是每个节点的父亲都是这个节点的最长后缀,所以一个节点的子树上所有节点都可以通过跳$fail$指针到达该节点
  • 所以统计一个字符串出现多少次就相当于其子树各个节点在建字典树时被经过的次数之和。建出$fail$树后$dfs$统计答案即可。
  • 查看代码
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int maxn = 1e6+5;
    const int mod = 1e9+7;
    ll qpow(ll a,ll b){ll res=1;for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
    struct graph
    {
        int head[maxn],nxt[maxn],to[maxn],w[maxn],sz;
        void init(){memset(head,-1,sizeof(head));}
        graph(){init();}
        void push(int a,int b,int c){nxt[sz]=head[a],to[sz]=b,w[sz]=c,head[a]=sz++;}
        int& operator[](const int a){return to[a];}
    }g;
    struct AC_automaton
    {
        int tree[maxn][26];
        int fail[maxn];
        int belong[maxn];
        int cnt[maxn];
        int tot;
        void Insert(char *s,int k)
        {
            int root = 0;
            for(int i = 0;s[i];++i){
                int p = s[i]-'a';
                if(!tree[root][p])tree[root][p]=++tot;
                root=tree[root][p];
                cnt[root]++;
            }
            belong[k]=root;
        }
        void get_fail()
        {
            queue<int>q;
            for(int i = 0;i < 26;++i){
                if(tree[0][i]){
                    q.push(tree[0][i]);
                }
            }
            while(!q.empty()){
                int now = q.front();
                q.pop();
                g.push(fail[now],now,0);
                for(int i = 0;i < 26;++i){
                    if(!tree[now][i]){
                        tree[now][i]=tree[fail[now]][i];
                    }
                    else {
                        q.push(tree[now][i]);
                        fail[tree[now][i]]=tree[fail[now]][i];
                    }
                }
            }
        }
        void dfs(int now)
        {   
            for(int i = g.head[now];~i;i = g.nxt[i]){
                dfs(g[i]);
                cnt[now]+=cnt[g[i]];
            }
        }
    }ac;
    char s[maxn];
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("simple.in", "r", stdin);
        freopen("simple.out", "w", stdout);
    #endif
        int n;
        scanf("%d",&n);
        for(int i = 1;i <= n;++i){
            scanf("%s",s);
            ac.Insert(s,i);
        }
        ac.get_fail();
        ac.dfs(0);
        for(int i = 1;i <= n;++i)printf("%d\n",ac.cnt[ac.belong[i]]);
        return 0;
    }
    
    

猜你喜欢

转载自www.cnblogs.com/aaddvvaanntteezz/p/12897684.html