POJ 2778 DNA Sequence (矩阵快速幂 + AC自动鸡)

题目:传送门

题意: 给你m个病毒串,只由(A、G、T、C) 组成, 问你生成一个长度为 n 的 只由 A、C、T、G 构成的,不包含病毒串的序列的方案数。

解: 对 m 个病毒串,建 AC 自动机, 然后, 这个AC自动机就类似于一张有向图, 可以用邻接矩阵存这张有向图。

  最多10个病毒串, 每个病毒串长度不超过 10, 那最多是个 100 * 100 的矩阵, 可以接受。

   最后用矩阵快速幂加速推导。

  

#include<cstdio>
#include<cstring>
#include<queue>
#define LL long long
#define rep(i, j, k) for(int i = j; i <= k; i++)
#define dep(i, j, k) for(int i = k; i >= j; i--)
#define INF 0x3f3f3f3f
#define inf 0x3f3f3f3f3f3f3f3f
#define mem(i, j) memset(i, j, sizeof(i))
#define pb push_back
using namespace std;

const int N = 111, mod = 100000;
struct mat {
    LL a[N][N];
    mat() { mem(a, 0); }
};
struct Trie {
    int ch[N][30], tot, metch[N], Fail[N];
    void init() {
        mem(ch[0], 0); tot = 1; metch[0] = 0;
    }
    int get(char Q) {
        if(Q == ‘A‘) return 0;
        else if(Q == ‘C‘) return 1;
        else if(Q == ‘T‘) return 2;
        return 3;
    }
    void join(char s[]) {
        int now = 0; int len = strlen(s);
        rep(i, 0, len - 1) {
            int id = get(s[i]);
            if(!ch[now][id]) {
                mem(ch[tot], 0); metch[tot] = 0;
                ch[now][id] = tot++;
            }
            now = ch[now][id];
        }
        metch[now] = 1;
    }
    void getFail() {
        queue<int> Q; while(!Q.empty()) Q.pop();
        rep(i, 0, 3) {
            if(ch[0][i]) {
                Q.push(ch[0][i]); Fail[ch[0][i]] = 0;
            }
        }
        while(!Q.empty()) {
            int now = Q.front(); Q.pop();
            rep(i, 0, 3) {
                int u = ch[now][i];
                if(u == 0) ch[now][i] = ch[Fail[now]][i];
                else {
                    Q.push(u);
                    Fail[u] = ch[Fail[now]][i];
                    metch[u] |= metch[Fail[u]];
                }
            }
        }
    }
    mat getMat() {
        mat A;
        rep(i, 0, tot - 1) {
            if(metch[i]) continue;
            rep(j, 0, 3) {
                if(!metch[ch[i][j]]) A.a[i][ch[i][j]]++;
            }
        }
        return A;
    }
};
Trie AC;
char b[25];
mat mul(mat A, mat B, int n) {
    mat C;
    rep(i, 0, n) {
        rep(j, 0, n) {
            rep(k, 0, n) {
                C.a[i][j] = (C.a[i][j] + A.a[i][k] * B.a[k][j]) % mod;
            }
        }
    }
    return C;
}
mat ksm(mat A, int B, int n) {
    mat res; rep(i, 0, n) res.a[i][i] = 1;
    while(B) {
        if(B & 1) res = mul(res, A, n);
        A = mul(A, A, n); B >>= 1;
    }
    return res;
}
int main() {
    int n, m;
    while(~scanf("%d %d", &m, &n)) {
        AC.init();
        rep(i, 1, m) {
            scanf("%s", b); AC.join(b);
        }
        AC.getFail();
        mat A; A = AC.getMat();
        mat ans = ksm(A, n, AC.tot - 1);
        LL res = 0LL;
        rep(i, 0, AC.tot - 1) {
            res = (res + ans.a[0][i]) % mod;
        }
        printf("%lld\n", res);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Willems/p/12004676.html

时间: 2024-10-03 23:07:31

POJ 2778 DNA Sequence (矩阵快速幂 + AC自动鸡)的相关文章

poj 2778 DNA Sequence(AC自动机+矩阵快速幂)

题目链接:poj 2778 DNA Sequence 题目大意:给定一些含有疾病的DNA序列,现在给定DNA长度,问有多少种不同的DNA序列是健康的. 解题思路:对DNA片段建立AC自动机,因为最多10个串,每个串最长为10,所以最多可能有100个节点,在长度为n时 以每个节点终止的健康字符串个数形成一个状态集,通过AC自动机形成的边可以推导出n+1的状态集,走到单词节点是 非法的,所以同样的我们可以先走到单词节点,但是从单词节点不向后转移.这样可以构造一个矩阵,剩下的就是矩阵 快速幂.注意的一

POJ POJ 2778 DNA Sequence AC自动机 + 矩阵快速幂

首先建立Trie和失败指针,然后你会发现对于每个节点 i 匹配AGCT时只有以下几种情况: i 节点有关于当前字符的儿子节点 j 且安全,则i 到 j找到一条长度为 1的路. i 节点有关于当前字符的儿子节点 j 且 不安全,则i 到 j没有路. i 节点没有关于当前字符的儿子节点 但是能通过失败指针找到一个安全的节点j,那么 i 到 j 找到一条长度为1的路. 关于节点安全的定义: 当前节点不是末节点且当前节点由失败指针指回跟节点的路径上不存在不安全节点,那么这个节点就是安全节点. 然后问题就

Uva10689 Yet another Number Sequence ( 矩阵快速幂 )

Uva 10689Yet another Number Sequence(  矩阵快速幂  ) 题意: 就是矩阵快速幂,没什么好说的. 分析: 其实还是斐波那契数列.只是最后对应的矩阵不是(1,1)是(a,b)了 MOD = 1; for( int i = 0; i < m; ++i ) MOD *= 10; 代码 #include <cstdio> #include <cstring> #include <algorithm> using namespace s

矩阵快速幂AC代码HDU 2035

#include <iostream> using namespace std;const int MOD = 1000;//像这样的一个常量就应该专门定义一下 int PowMod(int a, int n)//a^n%MOD { int ret = 1; while(n) { if(n & 1) ret = ret * a % MOD; //变为二进制,然后就可以专门进行分解计算,十分的方便,要求是结合位运算一同使用 a = a * a % MOD; //这里要求特别的注意,因为是

POJ 3070 Fibonacci(矩阵快速幂)

题目链接 题意 : 用矩阵相乘求斐波那契数的后四位. 思路 :基本上纯矩阵快速幂. 1 //3070 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 6 using namespace std; 7 8 struct Matrix 9 { 10 int v[2][2]; 11 }; 12 int n; 13 14 Matrix matrix_mul(Matrix a,Matrix b) 1

[POJ 3150] Cellular Automaton (矩阵快速幂 + 矩阵乘法优化)

Cellular Automaton Time Limit: 12000MS   Memory Limit: 65536K Total Submissions: 3048   Accepted: 1227 Case Time Limit: 2000MS Description A cellular automaton is a collection of cells on a grid of specified shape that evolves through a number of dis

POJ 2778 DNA Sequence(AC自动机确定DFA转移图+矩阵快速幂)

这道题极好的展示了AC自动机在构造转移图DFA上的应用 DFA转移图就是展示状态的转移过程的图,DFA图构造出来后就可以用DP求出任何DNA长度下,任何状态的个数 本题用自动机求出DFA矩阵,那么有 | dp[n][0] dp[n][1] ... dp[n][m] |=|dp[1][0] dp[1][1] ... dp[1][m] | * DFA^(n-1)    (m指状态总数) DP边界矩阵|dp[1][0] dp[1][1] ... dp[1][m] | 也就是DFA的第一行,所以dp[n

POJ 2778 DNA Sequence ( Trie图、矩阵快速幂 )

题意 : 给出一些病毒串,问你由ATGC构成的长度为 n 且不包含这些病毒串的个数有多少个 分析: 我们先分析Tire 图的结构 : Trie图是在AC自动机的原型上增添边使得状态可以快速转移,标记危险的节点(后缀是不良单词的节点): 那我们是想构造长度是n不包含不良串对不对 , 那是不是在trie图上从0节点走n步到安全节点的方案数(Trie图也是状态转移图) 在一个有向图中,A走k步到B的方案数(这显然是经典的矩阵快速幂问题),(原理需要自己搜索)先对原图建立一个邻接表M[i][j] , M

poj 3070 Fibonacci 【矩阵快速幂 求第N个斐波那契数%1000】

Fibonacci Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 11123   Accepted: 7913 Description In the Fibonacci integer sequence, F0 = 0, F1 = 1, and Fn = Fn ? 1 + Fn ? 2 for n ≥ 2. For example, the first ten terms of the Fibonacci sequenc