POJ2778 DNA Sequence(AC自动机 矩阵)

先使用AC自动机求得状态转移关系,再建立矩阵,mat[i][j]表示一步可从i到j且i,j节点均非终止字符的方案数,则此矩阵的n次方表示n步从i,到j的方法数。

 

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;

typedef long long LL;
const int N = 500, CH = 4;
struct Trie{
    Trie *next[CH];
    Trie *fail;
    int cnt, id;
}tree[N];

int Hash[128];
bool flag[1008];
int chi[108][4];

const LL MOD = 100000;
int num;
struct Mat{
    LL mat[108][108];

    Mat(){
        for(int i = 0; i <= num; i++){
            for(int j = 0; j <= num; j++){
                mat[i][j] = 0;
            }
        }
    }
    Mat operator*(const Mat &b){
        Mat res;
        for(int i = 0;i <= num; i++){
            for(int j = 0; j <= num; j++){
                for(int k = 0; k <= num; k++){
                    if(mat[i][k] == 0 || b.mat[k][j] == 0){
                        continue;
                    }
                    res.mat[i][j] += (mat[i][k] * b.mat[k][j])%MOD;
                }
                res.mat[i][j] %= MOD;
            }
        }
        return res;
    }
    Mat operator^(LL k){
        Mat res;
        Mat a = *this;
        for(int i = 0; i <= num; i++){
            res.mat[i][i] = 1;
        }
        while(k){
            if(k%2){
                res = res * a;
            }
            a = a * a;
            k /= 2;;
        }
        return res;
    }
};

class ACauto{
    public:
    int nxt;
    Trie *root;

    ACauto(){
        root = &tree[0];
        nxt=0;
        memset(&tree[0], 0, sizeof(Trie));
    }

    void insert(char *s){
        Trie *p = root;
        for(int i = 0; s[i]; i++){
            int c = Hash[s[i]];
            if(!p -> next[c]){
                memset(&tree[++nxt], 0, sizeof(Trie));
                p -> next[c] = &tree[nxt];
                p -> next[c] -> id = nxt;
            }
            p = p -> next[c];
        }
        p -> cnt++;
        flag[p -> id] = 1;
    }

    void build(){
        queue<Trie *> q;
        q.push(root);
        root -> fail = NULL;
        while(!q.empty()){
            Trie *cur = q.front();
            q.pop();
            //是否为终止结点
            if(cur ->fail && cur != root && flag[cur->fail->id]){
                flag[cur->id] = 1;
            }

            for(int i = 0; i < CH; i++){
                Trie *son = cur -> next[i];
                Trie *tp = (cur == root)? root: cur -> fail->next[i];
                if(son == NULL){
                    cur -> next[i] = tp;
                }else{
                    son -> fail = tp;
                    q.push(son);
                }
                son = cur -> next[i];
                //儿子节点
                chi[cur -> id][i] = son -> id;
            }
        }
    }

};

char str[1008];
int main(){
    Hash['A'] = 0;
    Hash['C'] = 1;
    Hash['G'] = 2;
    Hash['T'] = 3;
    int m;
    LL n;
    while(~scanf("%d %I64d", &m, &n)){
        ACauto ac;
        memset(flag, 0, sizeof(flag));
        memset(chi, -1, sizeof(chi));
        for(int i = 0; i < m; i++){
            scanf("%s", str);
            ac.insert(str);
        }
        ac.build();
        num = ac.nxt;
        Mat mt;
        //mat[i][j]表示一步可从i到j且i,j节点均非终止字符的方案数
        for(int i = 0; i <= num ; i++){
            for(int j  = 0; j < 4; j++){
                if(flag[chi[i][j]] == 0){
                    mt.mat[i][chi[i][j]]++;
                }
            }
        }
        mt = mt ^ n;
        LL res = 0;
        for(int i = 0; i <= num; i++){
            res = (res + mt.mat[0][i]) % MOD;
        }
        printf("%I64d\n", res);

    }
    return 0;
}

  

posted @ 2016-08-26 11:24  vwirtveurit  阅读(220)  评论(0编辑  收藏  举报