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

传送门:点击打开链接

题意:DNA只有AGCT四种脱氧核糖核苷酸组成,现在告诉你n条致病基因序列,,问长度为m的DNA序列里不含任何的致病基因的种类数是多少。

思路:这题可谓是AC自动机的经典神题。。如果只是简单的认为AC自动机只不过是在文中匹配字符串那就打错特错了,它还可以用来压缩状态~

这题我们先构想一下动态规划。

如果我们没学过AC自动机,,现在假如致病基因的长度都为3.我们可能会这样做。

设dp[i][j][k][l]表示为长度为i,最后是j,k,l结尾的字符串有多少种。那么我们可以列出转移方程。。

这样做法是最朴素的,会存在一个问题,如果致病基因比较长的时候,那就要设很多维的状态才行,所以对于这道题来说不合适。


假如我们已经用AC自动机,将所有的致病基因添加到自动机上了,现在的总节点数为s

那么,其实,任何字符串的末尾都能通过这s种状态表示出来!!如果字符串的后缀是和致病基因的前缀相等,那么此时致病基因匹配的前缀的结尾必然会有一个节点,这个节点就能表示出这个状态!那么如果字符串的后缀都没有致病基因与其配对呢,那就用根节点来表示,表示现在的字符串后缀没有与任何致病基因前缀匹配。这样,,我们就利用了AC自动机压缩完了所有的状态,只要再构造出邻接矩阵,就可以利用快速矩阵幂算出答案了。


邻接矩阵如何构造呢?直接看AC自动机的Next数组就可以了,看它的子节点是哪些,只要子节点的End等于0,就说明这个子节点不是叶子,不是单词的结尾,那么就能转移。

这里还存在一个wa点,如果End[Fail[u]]不等于0,说明对于u来说,u之前的那一条链和Fail[u]所在的Fail[u]之前的那一条链是相同的,但是在Fail[u]有结尾单词,所以u这个位置其实也是一个单词的结尾位置,我们可以手动加上标志End[u]=1。

#include<map>
#include<set>
#include<cmath>
#include<ctime>
#include<stack>
#include<queue>
#include<cstdio>
#include<cctype>
#include<string>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
#define fuck(x) cout<<"["<<x<<"]"
#define FIN freopen("input.txt","r",stdin)
#define FOUT freopen("output.txt","w+",stdout)
using namespace std;
typedef long long LL;

/*MX为总长度*/
const int MX = 200 + 5;
const int mod = 100000;
const int matMX = 150 + 5;

struct Mat {
    int m, n;
    LL S[matMX][matMX];
    Mat(int a, int b) {
        m = a;
        n = b;
        memset(S, 0, sizeof(S));
    }
    Mat(int a, int b, LL w[][matMX]) {
        m = a;
        n = b;
        for(int i = 0; i < m; i++) {
            for(int j = 0; j < n; j++) {
                S[i][j] = w[i][j];
            }
        }
    }
};

Mat mat_mul(Mat &A, Mat &B) {
    Mat C(A.m, B.n);
    for(int i = 0; i < A.m; i++) {
        for(int j = 0; j < B.n; j++) {
            for(int k = 0; k < A.n; k++) {
                C.S[i][j] = (C.S[i][j] + A.S[i][k] * B.S[k][j]) % mod;
            }
        }
    }
    return C;
}

Mat Blank(int m, int n) {
    Mat ret(m, n);
    for(int i = 0; i < m; i++) {
        ret.S[i][i] = 1;
    }
    return ret;
}

Mat mat_pow(Mat &A, LL b) {
    Mat ret = Blank(A.m, A.n);
    while(b) {
        if(b & 1) ret = mat_mul(ret, A);
        A = mat_mul(A, A);
        b >>= 1;
    }
    return ret;
}

struct AC_machine {
    int rear, root;
    int Next[MX][4], Fail[MX], End[MX];

    void Init() {
        rear = 0;
        root = New();
    }

    int New() {
        rear++;
        End[rear] = 0;
        for(int i = 0; i < 4; i++) {
            Next[rear][i] = -1;
        }
        return rear;
    }

    inline int ID(char x) {
        if(x == 'A') return 0;
        if(x == 'G') return 1;
        if(x == 'C') return 2;
        return 3;
    }

    void Add(char *A) {
        int n = strlen(A), now = root;
        for(int i = 0; i < n; i++) {
            int id = ID(A[i]);
            if(Next[now][id] == -1) {
                Next[now][id] = New();
            }
            now = Next[now][id];
        }
        End[now]++;
    }

    void Build() {
        queue<int>Q;
        Fail[root] = root;
        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 u = Q.front(); Q.pop();

            if(End[Fail[u]]) End[u] = 1;
            for(int i = 0; i < 4; i++) {
                if(Next[u][i] == -1) {
                    Next[u][i] = Next[Fail[u]][i];
                } else {
                    Fail[Next[u][i]] = Next[Fail[u]][i];
                    Q.push(Next[u][i]);
                }
            }
        }
    }

    Mat Query() {
        Mat A(rear, rear);
        for(int i = 1; i <= rear; i++) {
            for(int j = 0; j < 4; j++) {
                int chd = Next[i][j];
                if(End[chd] == 0) A.S[i - 1][chd - 1]++;
            }
        }
        return A;
    }
} AC;

char word[MX];

int main() {
    int m, n; //FIN;
    while(~scanf("%d%d", &m, &n)) {
        AC.Init();
        for(int i = 1; i <= m; i++) {
            scanf("%s", word);
            AC.Add(word);
        }
        AC.Build();

        Mat mat = AC.Query();
        Mat ret = mat_pow(mat, n);

        LL ans = 0;
        for(int i = 0; i < ret.m; i++) {
            ans = (ans + ret.S[0][i]) % mod;
        }
        printf("%I64d\n", ans);
    }
    return 0;
}


你可能感兴趣的:(AC自动机+快速矩阵幂 poj2778 DNA Sequence)