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;
    
    }
    
  • 相关阅读:
    Android学习之三:使用DDMS调试程序
    Android学习之二:使用Android文档帮助
    Android学习之四:创建一个简单程序
    Android学习之五:android一些基本控件
    创建Android开发环境
    Android学习之七:使用Container
    Android学习之六:使用Container
    IOSresign keyboard 新法儿
    IOStxt文件UTF8、UTF16格式
    IOSXMPP arc用方法fobjcarc
  • 原文地址:https://www.cnblogs.com/fridayfang/p/11090159.html
Copyright © 2011-2022 走看看