• 浅谈AC自动机


    标题是浅谈,那么我也就不过多的深入钻研了

    顶多算是说说定义,带大家看看代码

    首先,AC自动机,很多人看了这个算法名字,就自然的很慌,就不敢学

    其实静下心来,慢慢看,会发现其实比KMP还要容易理解(只不过是多模式串匹配,树形结构还很容易看出共同的前缀)

    AC自动机其实是在Trie树上进行一个类似于KMP的匹配操作(来自某Dalao)

    首先是要建一棵Trie字典树,具体如下:

    void pre(){
        int c=1;
        for(int i=1;i<=len;i++){
            int f=a[i]-'a';
            if(!trie[c][f]) trie[c][f]=++tot;//判断在之前是否出现过
            c=trie[c][f];
        }
        vis[c]++;//根据题目描述的不同改变
        return ;
    }

    然后是建立nxt数组,听着很难,我们不过多解释,只来说一下表面的意思

    我们现在有两个串A,B

    A:bb

    B:ba

    建立Trie树

    如下:

    然后我们先不着急,来想一想nxt数组具体代表的是什么,很多博客都只是讲了如何操作,我先省去那一部分,来说说为什么我们要记录一个nxt

    nxt[i]所代表的,适当你在节点i失配时,从哪里开始匹配

    相当于是在AC自动机上找到一个位置的前缀等于当前匹配到的串的后缀

    然后来看样例

    2因为没有与之相同的,所以直接指向1

    3是a,也没有相同的,也指向1

    最后是4,我们发现ab得前缀b是与之对应的,所以4指向2

    完成后如下:

    然后就是匹配了,和Trie树差不多,只不过是在失配的时候从nxt所指向的字符开始匹配

    上一道板子题

    试题描述
    给定 n 个长度不超过 50 的由小写英文字母组成的单词准备查询,以及一篇长为 m 的文章,问:文中出现了多少个待查询的单词。多组数据。
    输入
    第一行一个整数 T,表示数据组数;
    对于每组数据,第一行一个整数 n,接下去 n 行表示 n 个单词,最后一行输入一个字符串,表示文章。
    输出
    对于每组数据,输出一个数,表示文中出现了多少个待查询的单词。
    输入示例
    1
    5
    she
    he
    say
    shr
    her
    yasherhs
    输出示例
    3

    下面给出代码:

    #include<iostream>
    #include<algorithm>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<queue>
    using namespace std;
    inline long long rd(){
        long long x=0,f=1;
        char ch=getchar();
        for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
        for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
        return x*f;
    }
    inline void write(long long x){
        if(x<0) putchar('-'),x=-x;
        if(x>9) write(x/10);
        putchar(x%10+'0');
        return ;
    }
    int T;
    char a[10006];
    char s[1000006];
    int trie[10006][36];
    int vis[10006];
    int len=0;
    int tot=1;
    void pre(){
        int c=1;
        for(int i=1;i<=len;i++){
            int f=a[i]-'a';
            if(!trie[c][f]) trie[c][f]=++tot;
            c=trie[c][f];
        }
        vis[c]++;
        return ;
    }
    int nxt[100006];
    int ans=0;
    int q[100006];
    int l=0,r=0;
    void get_next(){
        l=0,r=0;
        for(int i=0;i<=25;i++) trie[0][i]=1;
        nxt[1]=0;
        q[++r]=1;
        while(l<r){
            int h=q[++l];
            for(int i=0;i<=25;i++){
                if(!trie[h][i]) trie[h][i]=trie[nxt[h]][i];//便于查找,我们建立一个虚点, 
                else{
                    nxt[trie[h][i]]=trie[nxt[h]][i];//一个点的nxt是它的父亲的nxt的和它一样的儿子,至于为什么,画个图就懂了 
                    q[++r]=trie[h][i];
                }
            }
        }
        return ;
    }
    void solve(){
        int c=1;
        for(int i=1;i<=len;i++){
            int f=s[i]-'a';
            int k=trie[c][f];
            while(k>1){
                if(vis[k]){
                    ans+=vis[k]; 
                    vis[k]=0;
                }
                k=nxt[k];
            }
            c=trie[c][f];
        }
        return ;
    }
    int main(){
        T=rd();
        while(T--){
            memset(vis,0,sizeof(vis));
            memset(trie,0,sizeof(trie));
            tot=1;
            ans=0;
            int n;
            n=rd();
            for(register int i=1;i<=n;i++){
                scanf("%s",a+1);
                len=strlen(a+1);
                pre();//建立Trie树 
            }
            scanf("%s",s+1);
            len=strlen(s+1);
            get_next();
            solve();
            write(ans);
            puts("");
        }
        return 0;
    }
    蒟蒻总是更懂你✿✿ヽ(°▽°)ノ✿
  • 相关阅读:
    判断有向无环图(DAG)
    单向连通图 Going from u to v or from v to u? poj2762
    百度地图的实时路况 2016 计蒜之道 复赛
    快速模取幂
    fibonacci数列(二)_矩阵快速幂
    数与矩阵快速幂基本知识
    Brute-force Algorithm_矩阵快速幂&&欧拉公式*****
    Nearest number
    Zipper_DP
    Jumping Cows_贪心
  • 原文地址:https://www.cnblogs.com/WWHHTT/p/9747616.html
Copyright © 2020-2023  润新知