zoukankan      html  css  js  c++  java
  • AC自动机

    AC自动机

    总结

    AC自动机可以说是KMP和Trie树的合体,用于解决多模式串匹配问题

    核心过程:

    • 建立trie树,插入字符串

    • 完成fail指针并完成(next[i][j])(对于j不存在的情况)

    void build(){//bfs 完成 fail指针
            queue<int> q;
            fail[root] = root;//规定fail[root]指向root
            for(int i=0;i<26;i++){
                if(nxt[root][i]==-1){ nxt[root][i]=root; }//root 下面的空指针全部只想root
                else{
                    fail[nxt[root][i]] = root;  //36,38这里保证了47行代码是合理的;
                    q.push(nxt[root][i]);
                }
            }
            while(!q.empty()){
                int now = q.front();
                ///db(now);
                q.pop();
                for(int i=0;i<26;i++){
                    int t = nxt[now][i];
                    if(t==-1){nxt[now][i] = nxt[fail[now]][i];}//nxt和fail是相互作用的
                    else {
                        fail[t] = nxt[fail[now]][i];  // 47,49两行决定了其如同KMP算法,能够将复杂度维持在线性; 仔细思考
                        q.push(t);
                    }
                }
            }
    
        }
    
    • 进行查询,注意统计种类还是次数;
      统计时,注意其后缀也可能是字符串
    code
    int query(char buf[]){//目前反对空串
            int len = strlen(buf);
            int now = root,cnt = 0;
            for(int i=0;i<len;i++){
                now = nxt[now][idx(buf[i])];
                int t = now;
                while(t!=root){
                    cnt+=end[t];
                    end[t]=0;//只统计种类
                    t = fail[t];// 后缀也可能被统计
                }
    
            }
            return cnt;
        }
    

    模板题HDU2222

    code

    test

    #include<bits/stdc++.h>
    #include<stdio.h>
    #include<iostream>
    using namespace std;
    #define db(x) cout<<"["<<#x<<"]="<<x<<endl
    const int maxm = 1e6+10;
    const int maxn = 5e5+20;
    char text[maxm],s[60];//s: 模式串
    int ca,n;
    struct Aho{// 数组形式的AC自动机
        int nxt[maxn][26],fail[maxn],end[maxn];//end 用于计数
        // nxt[i][j]==-1 表示没有字符 'a'+j
        int root,L;//L 控制节点计数
        int newp(){
            for(int i=0;i<26;i++){nxt[L][i]=-1;} //fail?
            end[L]=0, L++;
            return L-1;
        }
        void init(){
            L=0;
            root = newp();
        }
        inline int idx(char c){return c - 'a';}
        void insert(char p[]){
            int len = strlen(p);
            int now = root;
            for(int i=0;i<len;i++){
                int t = nxt[now][idx(p[i])];
                if(t==-1){nxt[now][idx(p[i])] = newp();}
                now = nxt[now][idx(p[i])];
            }
            if(now!=root) end[now]++; //空字符串 now == root
        }
        void build(){//bfs 完成 fail指针
            queue<int> q;
            fail[root] = root;//规定fail[root]指向root
            for(int i=0;i<26;i++){
                if(nxt[root][i]==-1){ nxt[root][i]=root; }//root 下面的空指针全部只想root
                else{
                    fail[nxt[root][i]] = root;  //36,38这里保证了47行代码是合理的;
                    q.push(nxt[root][i]);
                }
            }
            while(!q.empty()){
                int now = q.front();
                ///db(now);
                q.pop();
                for(int i=0;i<26;i++){
                    int t = nxt[now][i];
                    if(t==-1){nxt[now][i] = nxt[fail[now]][i];}//nxt和fail是相互作用的
                    else {
                        fail[t] = nxt[fail[now]][i];  // 47,49两行决定了其如同KMP算法,能够将复杂度维持在线性; 仔细思考
                        q.push(t);
                    }
                }
            }
    
        }
        int query(char buf[]){//目前反对空串
            int len = strlen(buf);
            int now = root,cnt = 0;
            for(int i=0;i<len;i++){
                now = nxt[now][idx(buf[i])];
                int t = now;
                while(t!=root){
                    cnt+=end[t];
                    end[t]=0;//只统计种类
                    t = fail[t];// 后缀也可能被统计
                }
    
            }
            return cnt;
        }
        void debug(){
            for(int i=0;i<L;i++){
                printf("id=%3d fail=%3d end=%3d chi=[",i,fail[i],end[i]);
                for(int j=0;j<26;j++){
                    printf("%c %d,",j+'a',nxt[i][j]);
                }
                printf("]
    ");
            }
        }
    
    }ac;
    int main(){
        //freopen("in.txt","r",stdin);
        //freopen("out.txt","w",stdout);
        scanf("%d",&ca);
        while(ca--){
            ac.init();
            scanf("%d",&n);
            for(int i=0;i<n;i++){
                scanf("%s",s);
                ac.insert(s);
            }
            ac.build();
            //ac.debug();
            scanf("%s",text);
            printf("%d
    ",ac.query(text));
        }
        return 0;
    
    }
    
  • 相关阅读:
    Codeforces 691A Fashion in Berland
    HDU 5741 Helter Skelter
    HDU 5735 Born Slippy
    HDU 5739 Fantasia
    HDU 5738 Eureka
    HDU 5734 Acperience
    HDU 5742 It's All In The Mind
    POJ Euro Efficiency 1252
    AtCoder Beginner Contest 067 C
    AtCoder Beginner Contest 067 D
  • 原文地址:https://www.cnblogs.com/fridayfang/p/11090159.html
Copyright © 2011-2022 走看看