Loading

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("]\n");
        }
    }

}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\n",ac.query(text));
    }
    return 0;

}
posted @ 2019-06-26 15:32  fridayfang  阅读(128)  评论(0编辑  收藏  举报