• 【POJ-2778】DNA Sequence(DP转移+矩阵快速幂+AC自动机)


    题目连接:https://vjudge.net/problem/POJ-2778

    题目大意

    给定 (m) 个字符串,问构造一个长度为 (n) 的字符串,使其不含 (m) 个字符串,问构造方法种类。

    思路

    AC自动机和矩阵快速幂部分这篇博客已经说的很好了,个人主要是 AC自动机病毒后缀的转移DP递推式 卡了很久。

    首先是AC自动机上病毒后缀的转移,AC自动机的fail指针指向的是指以节点 (x) 结尾的文本串在其他模式串中所能匹配的最长前缀的地方。

    若其fail指针所指向的地方为 (m)个字符串的结尾,那么其也不可达,在 (build) 时能够保证顺序。

    然后是DP递推式,设 (dp[i][j]) 为走了(j) 步到达节点 (i) 的方案数,显然 (dp[0][0]=1),当 (i eq 0) 时,(dp[i][0]=0)

    (a[i][j]) 为从节点 (i) 到达节点 (j) 是否存在边。

    最终得 (dp[i][j] = sum_{x=0}^{N} a[x][i] * dp[x][j-1]),这部分可以用矩阵快速幂解决。

    AC代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    
    typedef long long ll;
    using namespace std;
    
    const int MAXN = 1e6 + 5;
    const int MAXM = 110;
    const int mod = 100000;
    
    class mat {
    public:
        int n, m;
        int v[MAXM][MAXM];
    
        mat(int n, int m) : n(n), m(m) {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++)
                    v[i][j] = 0;
            }
        }
    
        void init() {
            for (int i = 0; i < n; i++) {
                for (int j = 0; j < m; j++)
                    v[i][j] = 0;
            }
        }
    
        void init1() {
            for (int i = 0; i < n; i++)
                for (int j = 0; j < m; j++)
                    v[i][j] = (i == j); //单位矩阵
        }
    
        mat operator*(const mat &B) const {//矩阵乘法 A(n,k)*B(k,m)=C(n,m);
            mat C(n, B.m);
            C.init();
            for (int i = 0; i < n; i++)
                for (int j = 0; j < B.m; j++)
                    for (int k = 0; k < m; k++)
                        C.v[i][j] = (C.v[i][j] + (ll) v[i][k] * B.v[k][j] % mod) % mod;//Mod
            return C;
        }
    
        mat operator^(int t) {//矩阵快速幂 n=m时可用
            mat ans(n, n), now(n, n);
            ans.init1();
            for (int i = 0; i < n; i++)
                for (int j = 0; j < n; j++)
                    now.v[i][j] = v[i][j];
            while (t > 0) {
                if (t & 1) ans = ans * now;
                now = now * now;
                t >>= 1;
            }
            return ans;
        }
    
        void debug() {
            for (int i = 0; i <= n; i++) {
                for (int j = 0; j <= n; j++) {
                    printf("%d ", v[i][j]);
                }
                printf("
    ");
            }
        }
    
    };
    
    
    int char2id(char ch) {
        switch (ch) {
            case 'A':
                return 0;
            case 'T':
                return 1;
            case 'G':
                return 2;
            case 'C':
                return 3;
        }
    }
    
    class AC {
    public:
        int T[MAXN][4], top;
        bool endpos[MAXN];
        int fail[MAXN];
        queue<int> q;
    
        void init() {
            top = 1;
            memset(T[0], 0, sizeof(T[0]));
            endpos[0] = fail[0] = 0;
        }
    
        void insert(char str[], int lenstr) {
            int u = 0;
            for (int i = 1; i <= lenstr; i++) {
                int ch = char2id(str[i]);
                if (!T[u][ch]) {
                    endpos[top] = fail[top] = 0;    // init a node
                    memset(T[top], 0, sizeof(T[top]));
                    T[u][ch] = top++;
                }
                u = T[u][ch];
            }
            endpos[u] = 1;
        }
    
        void build() {
            for (int i = 0; i < 4; i++)
                if (T[0][i]) {
                    fail[T[0][i]] = 0;
                    q.push(T[0][i]);
                }
            while (!q.empty()) {
                int u = q.front();
                q.pop();
                if (endpos[fail[u]]) endpos[u] = 1;
                for (int i = 0; i < 4; i++)
                    if (T[u][i]) {
                        fail[T[u][i]] = T[fail[u]][i];
                        q.push(T[u][i]);
                    } else T[u][i] = T[fail[u]][i];
            }
        }
    
        void build_mat(mat &mat) {
            mat.init();
            for (int i = 0; i < top; i++) {
                if (endpos[i]) continue;
                for (int j = 0; j < 4; j++) {
                    if (!endpos[T[i][j]]) mat.v[i][T[i][j]]++;
                }
            }
        }
    
    } ac;
    
    char str[MAXN];
    
    
    int main() {
    
        int m, n;
        while (~scanf("%d%d", &m, &n)) {
            ac.init();
            while (m--) {
                scanf("%s", str + 1);
                ac.insert(str, strlen(str + 1));
    
            }
            ac.build();
    
    
            mat ma(ac.top, ac.top);
            ac.build_mat(ma);
    
            ma = ma ^ n;
            int res = 0;
            for (int i = 0; i < ac.top; i++) {
                res = (res + ma.v[0][i]) % mod;
            }
            printf("%d
    ", res);
        }
    }
    
    /*
    2 10
    AG
    CG
     */
    
  • 相关阅读:
    mysqldump指定编码导出数据
    centos 自带mysql卸载时出现无法卸载情况
    Linux下如何彻底删除MySQL
    输出一行字符串中的最长单词---调用函数
    span设置固定宽度
    如何使用Reaver破解Wi-Fi网络的WPA密码
    kernel hexdump分析 (2.0)
    C++基础学习笔记----第七课(面向对象的基本概念)
    有关java中的final关键字
    测试framebuffer
  • 原文地址:https://www.cnblogs.com/tudouuuuu/p/13986981.html
Copyright © 2020-2023  润新知