AC自动机(模板) LUOGU P3808

传送门

解题思路

AC自动机,是解决多模匹配问题的算法,是字典树与kmp结合的算法,可以解决许多子串在文本串中出现的次数等信息。关键是实现一个fail指针,是指向更靠上的前缀相同字母,从而可以实现在文本串中跳的操作。

代码

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<queue>

    using namespace std;
    const int MAXN = 1e6+5;

    int n,ans,cnt;

    struct Tree{
        int vis[30],fail,end;
    }AC[MAXN];

    inline void build(string S){
        int len=S.length();
        int now=0;
        for(register int i=0;i<len;i++){
            if(!AC[now].vis[S[i]-'a'+1])
                AC[now].vis[S[i]-'a'+1]=++cnt;
            now=AC[now].vis[S[i]-'a'+1];
        }
        AC[now].end++;
    }

    inline void Get_fail(){
        queue<int> Q;
        for(register int i=1;i<=26;i++)
            if(AC[0].vis[i]){
                AC[AC[0].vis[i]].fail=0;
                Q.push(AC[0].vis[i]);
            }
        while(Q.size()){
            int x=Q.front();Q.pop();
            for(register int i=1;i<=26;i++){
                if(AC[x].vis[i]){   //如果这个字母存在,子节点fail指针指向它上一层这个字母的fail所指向的字母,有点难理解,要画图
                    AC[AC[x].vis[i]].fail=AC[AC[x].fail].vis[i];
                    Q.push(AC[x].vis[i]);
                }
                else AC[x].vis[i]=AC[AC[x].fail].vis[i];
            }
        }
    }

    inline int Query(string S){
        int len=S.length();
        int now=0,t;
        for(register int i=0;i<len;i++){
            now=AC[now].vis[S[i]-'a'+1];
            for(t=now;t && AC[t].end!=-1;t=AC[t].fail){
                ans+=AC[t].end;
                AC[t].end=-1;
            }
        }
        return ans;
    }

    int main(){
        cin>>n;
        for(register int i=1;i<=n;i++){
            string s;
            cin>>s;
            build(s);
        }
        AC[0].fail=0;
        Get_fail();
        string s;
        cin>>s;
        cout<<Query(s)<<endl;
        return 0;
    }
posted @ 2018-07-16 14:34  Monster_Qi  阅读(80)  评论(0编辑  收藏  举报