DNA Sequence POJ - 2778(AC自動機+矩陣快速冪)

It’s well known that DNA Sequence is a sequence only contains A, C, T and G, and it’s very useful to analyze a segment of DNA Sequence,For example, if a animal’s DNA sequence contains segment ATC then it may mean that the animal may have a genetic disease. Until now scientists have found several those segments, the problem is how many kinds of DNA sequences of a species don’t contain those segments.

Suppose that DNA sequences of a species is a sequence that consist of A, C, T and G,and the length of sequences is a given integer n.
Input
First line contains two integer m (0 <= m <= 10), n (1 <= n <=2000000000). Here, m is the number of genetic disease segment, and n is the length of sequences.

Next m lines each line contain a DNA genetic disease segment, and length of these segments is not larger than 10.
Output
An integer, the number of DNA sequences, mod 100000.
Sample Input
4 3
AT
AC
AG
AA
Sample Output
36

題意:
只由 A G C T組成的長度爲n序列,有一些組合不能出現,求合法序列的數目。

思路:
這是一道很綜合的題目!

1. 建圖

在使用給定的字符串跑完AC自動機以後,實際上就可以把Trie樹當成一個有向圖。
題意可以轉換爲:從root節點(0號)開始,走n步且不會走到有序列結尾的節點的方案數。

舉個栗子,當n=0n=0的時候,那0的子樹的fail指針全部指向自己,每次都有4種可能,所以答案是 4n4^n

當序列爲 AG,CG,所得圖爲
在這裏插入圖片描述
其中2和4是結尾點,不需要考慮。

2. 狀態定義

  1. 定義 F[i][j]F[i][j]代表在第ii個點,已經走了jj步的方案數。
  2. 易得 F[0][0]=1,F[0][1]=F[0][3]=0F[0][0]=1,F[0][1]=F[0][3]=0
  3. 轉移方程爲 F[i][j]=ai0F[0][j1]+ai1F[1][j1]+ai2F[2][j1]...F[i][j]=ai0 * F[0][j-1] + ai1 * F[1][j-1] + ai2*F[2][j-1]...
  4. 其中aijaij代表圖中從iijj的邊數。如上面那個例子的矩陣爲:
  5. 2 1 0 1 0
    1 1 0 1 0
    0 0 0 0 0
    1 1 0 1 0
    0 0 0 0 0

3. 矩陣遞推

有沒有發現,狀態轉移方程特別像矩陣乘法?

沒看出來?








現在再看呢?
在這裏插入圖片描述
(靈魂畫手.jpg…

這個矩陣方程,和上面的遞推方程等價。
但表示成矩陣,則加速了這個遞推的過程。

4. 總結

所以到上一步就結束了。

  1. 我們先用AC自動機建好圖,去除結尾點(包括Fail指針能到結尾點的,因爲Fail指針對應有結尾點,意味着當前對應後綴爲結尾,那當前節點肯定不能用了),
  2. 然後算出點之間邊數的矩陣,
  3. 推出一個狀態方程,這個轉移方程可以轉換爲矩陣乘法,
  4. 最後再用矩陣快速冪加速遞推這個過程。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>

using namespace std;

const int maxn = 1e6 + 7;
const int mod = 1e5;
typedef long long ll;

struct Trie {
    int son[30],val,fail;
}t[maxn];

struct Matrix {
    ll m[101][101];
}M;

ll m;
int n,cnt,id[505];
char s[maxn];

void init() {
    for(int i = 0;i <= cnt;i++) {
        memset(t[i].son,0,sizeof(t[i].son));
        t[i].val = t[i].fail = 0;
    }
    cnt = 0;
    id['A'] = 0;
    id['G'] = 1;
    id['C'] = 2;
    id['T'] = 3;
}

void insert(char *s) {
    int u = 0,len = strlen(s);
    for(int i = 0;i < len;i++) {
        int v = id[s[i]];
        if(!t[u].son[v]) t[u].son[v] = ++cnt;
        u = t[u].son[v];
    }
    t[u].val++;
}

void getFail() {
    queue<int>q;
    q.push(0);t[0].fail = 0;
    while(!q.empty()) {
        int u = q.front();q.pop();
        if(t[t[u].fail].val) {
            t[u].val = 1;
        }
        for(int i = 0;i < 4;i++) {
            int v = t[u].son[i];
            int Fail = t[u].fail;
            if(!v) {
                t[u].son[i] = t[Fail].son[i];continue; //直接讓不存在的兒子指向失配位置
            }
            if(Fail == 0 && t[Fail].son[i] == v) t[v].fail = 0;
            else t[v].fail = t[Fail].son[i];
            q.push(v);
        }
    }
}

void build() {
    for(int i = 0;i <= cnt;i++) {
        for(int j = 0;j < 4;j++) {
            if(!t[i].val && !t[t[i].son[j]].val) {
                M.m[i][t[i].son[j]]++;
            }
        }
    }
}

Matrix mul(Matrix A,Matrix B) {
    Matrix C;
    memset(C.m,0,sizeof(C.m));
    for(int i = 0;i <= cnt;i++) {
        for(int j = 0;j <= cnt;j++) {
            C.m[i][j] = 0;
            for(int k = 0;k <= cnt;k++) {
                C.m[i][j] = (C.m[i][j] + A.m[i][k] * B.m[k][j]) % mod;
            }
        }
    }
    return C;
}

Matrix Pow(Matrix A,ll n) {
    Matrix res;
    memset(res.m,0,sizeof(res.m));
    for(int i = 0;i <= cnt;i++) res.m[i][i] = 1;
    while(n) {
        if(n & 1) {
            res = mul(res,A);
        }
        A = mul(A,A);
        n >>= 1;
    }
    return res;
}

int main() {
    init();
    scanf("%d %lld",&n,&m);
    for(int i = 1;i <= n;i++) {
        scanf("%s",s);
        insert(s);
    }
    getFail();
    build();
    Matrix res = Pow(M,m);
    ll ans = 0;
    for(int i = 0;i <= cnt;i++) {
        ans = (ans + res.m[0][i]) % mod;
    }
    printf("%lld\n",ans);
    return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章