• AC自动机(模板+例题)


    首先要明白AC自动机是干什么的:

    AC自动机其实就是一种多模匹配算法,那么你可能会问什么叫做多模匹配算法。下面是我对多模匹配的理解,与多模与之对于的是单模,单模就是给你一个单词,然后给你一个字符串,问你这个单词是否在这个字符串中出现过(匹配),这个问题可以用kmp算法在比较高效的效率上完成这个任务。那么现在我们换个问题,给你很多个单词,然后给你一段字符串,问你有多少个单词在这个字符串中出现过,当然我们暴力做,用每一个单词对字符串做kmp,这样虽然理论上可行,但是时间复杂度非常之高,当单词的个数比较多并且字符串很长的情况下不能有效的解决这个问题,所以这时候就要用到我们的ac自动机算法了。

    一个母字符串,多个子字符串与其多次匹配,就是它的用处。

    这里有很多例题:AC自动机小结


    例题1:HDU2222

    //======================
    // HDU 2222
    // 求目标串中出现了几个模式串
    //====================
    #include <stdio.h>
    #include <algorithm>
    #include <iostream>
    #include <string.h>
    #include <queue>
    using namespace std;
    
    struct Trie
    {
        int next[500010][26],fail[500010],end[500010];
        int root,L;
        int newnode()
        {
            for(int i = 0;i < 26;i++)
                next[L][i] = -1;
            end[L++] = 0;
            return L-1;
        }
        void init()
        {
            L = 0;
            root = newnode();
        }
        void insert(char buf[])
        {
            int len = strlen(buf);
            int now = root;
            for(int i = 0;i < len;i++)
            {
                if(next[now][buf[i]-'a'] == -1)
                    next[now][buf[i]-'a'] = newnode();
                now = next[now][buf[i]-'a'];
            }
            end[now]++;
        }
        void build()
        {
            queue<int>Q;
            fail[root] = root;
            for(int i = 0;i < 26;i++)
                if(next[root][i] == -1)
                    next[root][i] = root;
                else
                {
                    fail[next[root][i]] = root;
                    Q.push(next[root][i]);
                }
            while( !Q.empty() )
            {
                int now = Q.front();
                Q.pop();
                for(int i = 0;i < 26;i++)
                    if(next[now][i] == -1)
                        next[now][i] = next[fail[now]][i];
                    else
                    {
                        fail[next[now][i]]=next[fail[now]][i];
                        Q.push(next[now][i]);
                    }
            }
        }
        int query(char buf[])
        {
            int len = strlen(buf);
            int now = root;
            int res = 0;
            for(int i = 0;i < len;i++)
            {
                now = next[now][buf[i]-'a'];
                int temp = now;
                while( temp != root )
                {
                    res += end[temp];
                    end[temp] = 0;//如果这里没有清0(删去),那么就会变成重复统计
                    temp = fail[temp];
                }
            }
            return res;
        }
        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("%2d",next[i][j]);
                printf("]
    ");
            }
        }
    };
    char buf[1000010];
    Trie ac;
    int main()
    {
        int T;
        int n;
        scanf("%d",&T);
        while( T-- )
        {
            scanf("%d",&n);
            ac.init();
            for(int i = 0;i < n;i++)
            {
                scanf("%s",buf);
                ac.insert(buf);
            }
            ac.build();
            scanf("%s",buf);
            printf("%d
    ",ac.query(buf));
        }
        return 0;
    }

    例题2:HDU5384
    给你一堆母串,还有另一堆子串,询问每个母串中出现的子串总次数。(子串计数可重叠)

    注:下面这个代码是阉割版的,需要将a变为a[maxn]数组同时遍历每个母串才可以使用。

    #include<cstdio>
    #include<cstring>
    #include<string.h>
    #include<algorithm>
    #include<queue>
    using namespace std;
    
    const int maxn = 1e6 + 5;
    int n;
    char a[maxn];
    char s[maxn];
    
    struct trie {
        int next[maxn][26], fail[maxn], end[maxn];
        int root, cnt;
        int new_node() {
            memset(next[cnt], -1, sizeof next[cnt]);
            end[cnt++] = 0;
            return cnt - 1;
        }
        void init() {
            cnt = 0;
            root = new_node();
        }
        void insert(char *buf) {//字典树插入一个单词
            int len = strlen(buf);
            int now = root;
            for (int i = 0; i < len; i++) {
                int id = buf[i] - 'a';
                if (next[now][id] == -1) {
                    next[now][id] = new_node();
                }
                now = next[now][id];
            }
            end[now]++;
        }
        void build() {//构建fail指针
            queue <int> q;
            fail[root] = root;
            for (int i = 0; i < 26; i++) {
                if (next[root][i] == -1) {
                    next[root][i] = root;
                }
                else {
                    fail[next[root][i]] = root;
                    q.push(next[root][i]);
                }
            }
            while (!q.empty()) {
                int now = q.front(); q.pop();
                for (int i = 0; i < 26; i++) {
                    if (next[now][i] == -1) {
                        next[now][i] = next[fail[now]][i];
                    }
                    else {
                        fail[next[now][i]] = next[fail[now]][i];
                        q.push(next[now][i]);
                    }
                }
            }
        }
        int query(string buf) {
            int len = buf.length();
            int now = root;
            int res = 0;
            for (int i = 0; i < len; i++) {
                int id = buf[i] - 'a';
                now = next[now][id];
                int tmp = now;
                while (tmp != root) {
                    res += end[tmp];
                    tmp = fail[tmp];
                }
            }
            return res;
        }
    }ac;
    
    int main() {
        int T;
        scanf("%d", &T);
        while (T--) {
            ac.init();
            scanf("%s", a);
            scanf("%d", &n);
            for (int i = 0; i < n; i++) {
                scanf("%s", s);
                ac.insert(s);
            }
            ac.build();
            printf("%d
    ", ac.query(a));
        }
        return 0;
    }

    例题3:POJ2778
    神仙打架、神仙打架。。。。
    题意:

    •题意:有m种DNA序列是有疾病的,问有多少种长度为n的DNA序列不包含任何一种有疾病的DNA序列。(仅含A,T,C,G四个字符)
    •样例m=4,n=3,{“AA”,”AT”,”AC”,”AG”}
    •答案为36,表示有36种长度为3的序列可以不包含疾病

    简单来说就是:
    算了我不说了。。。。看上面的讲解和代码吧

    //https://blog.csdn.net/u013446688/article/details/47378255
    #include <iostream>  
    #include <cstdio>  
    #include <queue>  
    #include <cstring>  
    using namespace std;  
    
    const int MOD = 100000;  
    struct Matrix{  
        int mat[110][110], n;  
    
        Matrix(){}  
    
        Matrix(int _n){  
            n = _n;  
            for(int i = 0; i < n; i++)  
                for(int j = 0; j < n; j++)  
                    mat[i][j] = 0;  
        }  
    
        Matrix operator *(const Matrix &b) const{  
            Matrix ret = Matrix(n);  
            for(int i = 0; i < n; i++)  
                for(int j = 0; j < n; j++)  
                    for(int k = 0; k < n; k++){  
                        int tmp = (long long)mat[i][k] * b.mat[k][j] % MOD;  
                        ret.mat[i][j] = (ret.mat[i][j] + tmp) % MOD;  
                    }  
            return ret;  
        }  
    };  
    
    struct Trie{  
        int next[110][4], fail[110];  
        bool end[110];  
        int root, L;  
    
        int newnode(){  
            for(int i = 0; i < 4; i++) next[L][i] = -1;  
            end[L++] = false;  
            return L-1;  
        }  
    
        void init(){  
            L = 0;  
            root = newnode();  
        }  
    
        int getch(char ch){  
            if(ch == 'A') return 0;  
            if(ch == 'C') return 1;  
            if(ch == 'G') return 2;  
            else return 3;  
        }  
    
        void insert(char s[]){  
            int len = strlen(s);  
            int now = root;  
            for(int i = 0; i < len; i++){  
                if(next[now][getch(s[i])] == -1)  
                    next[now][getch(s[i])] = newnode();  
                now = next[now][getch(s[i])];  
            }  
            end[now] = true;  
        }  
    
        void build(){  
            queue<int> Q;  
            for(int i = 0; i < 4; i ++){  
                if(next[root][i] == -1) next[root][i] = root;  
                else{  
                    fail[ next[root][i] ] = root;  
                    Q.push(next[root][i]);  
                }  
            }  
            while(!Q.empty()){  
                int now = Q.front();  
                Q.pop();  
                if(end[ fail[now] ] == true) end[now] = true;  
                for(int i = 0; i < 4; i ++){  
                    if(next[now][i] == -1)  
                        next[now][i] = next[ fail[now] ][i];  
                    else{  
                        fail[ next[now][i] ] = next[ fail[now] ][i];  
                        Q.push(next[now][i]);  
                    }  
                }  
            }  
        }  
    
        Matrix getMatrix(){  
            Matrix ret = Matrix(L);  
            for(int i = 0; i < L; i ++)  
                for(int j = 0; j < 4; j ++)  
                    if(end[ next[i][j] ] == false)  
                        ret.mat[i][ next[i][j] ] ++;  
            return ret;  
        }  
    };  
    
    
    Trie ac;  
    char buf[20];  
    
    Matrix pow_Mat(Matrix a, int n){    //快速幂  
        Matrix ret = Matrix(a.n);  
        for(int i = 0; i < ret.n; i ++) ret.mat[i][i] = 1;  
        Matrix tmp = a;  
        while(n){  
            if(n & 1) ret = ret * tmp;  
            tmp = tmp * tmp;  
            n >>= 1;  
        }  
        return ret;  
    }  
    
    int main(){  
        #ifdef sxk  
            freopen("in.txt", "r", stdin);  
        #endif //sxk  
    
        int n, m;  
        while(scanf("%d%d", &m, &n) == 2){  
            ac.init();  
            for(int i = 0; i < m; i ++){  
                scanf("%s", buf);  
                ac.insert(buf);  
            }  
            ac.build();  
            Matrix a = ac.getMatrix();  
            a = pow_Mat(a, n);  
            int ans = 0;  
            for(int i = 0; i < a.n; i ++)  
                ans = (ans + a.mat[0][i]) % MOD;  
            printf("%d
    ", ans);  
        }  
        return 0;  
    }  
  • 相关阅读:
    HTML-DOM实例——实现带样式的表单验证
    HTML-DOM常用对象的用法(select/option/form/table)
    class介绍
    let 和const命令
    页面滚动事件和利用JS实现回到顶部效果
    DOM的利用冒泡做的一个小程序
    BOM的对象总结(location,screen,navigator,history)
    IE下的双外边距浮动bug
    全国计算机三级网络工程技术复习笔记2
    全国计算机三级网络工程技术复习笔记1
  • 原文地址:https://www.cnblogs.com/romaLzhih/p/9489820.html
Copyright © 2020-2023  润新知