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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/OscaronMar/article/details/82924250

题目给m个病毒串,问不包含病毒串的长度n的DNA片段有几个。

感觉这题好神,看了好久的题解。

所有病毒串构造一个AC自动机,这个AC自动机可以看作一张有向图,图上的每个顶点就是Trie树上的结点,每个结点都可以看作是某个病毒串的前缀,Trie树的根则是空字符串。

而从根出发,在AC自动机上跑,经过k次转移到达某个结点,这个结点所代表的病毒串前缀可以看作长度为k的字符串的后缀,如果接下去跑往ATCG四个方向转移,就能到达新的结点,转移到新的长k+1字符串的后缀。

这样带着一个后缀状态的转移就能绕开病毒串,所以病毒串末尾的结点要标记,后缀存在病毒串的结点也要标记(这个在计算结点fail的时候就能处理),转移时就不能转移到被标记的结点。

接下来,题目的数据范围是10个长度10的病毒串,所以Trie树中最多101左右个结点,那么AC自动机整个转移就可以构建一张101*101的邻接矩阵,矩阵i行j列的权值是结点i转移到结点j的方案数。而进行k次转移,从结点i转移到结点j的方案数是这个矩阵的k次幂,这个结论离散数学的图论有。。

所以,长度n的字符串的方案数,就是转移n次根结点能到所有结点的方案和就是答案。就是计算矩阵的n次幂,统计根所在行的数字和,n的达到20亿用矩阵快速幂即可。

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int ch[111][4],fail[111],tn;
bool flag[111];

int idx[128];
void insert(char *s){
    int x=0;
    for(int i=0; s[i]; ++i){
        int y=idx[s[i]];
        if(ch[x][y]==0) ch[x][y]=++tn;
        x=ch[x][y];
    }
    flag[x]=1;
}
void init(){
    memset(fail,0,sizeof(fail));
    queue<int> que;
    for(int i=0; i<4; ++i){
        if(ch[0][i]) que.push(ch[0][i]);
    }
    while(!que.empty()){
        int now=que.front(); que.pop();
        for(int i=0; i<4; ++i){
            if(ch[now][i]) que.push(ch[now][i]),fail[ch[now][i]]=ch[fail[now]][i];
            else ch[now][i]=ch[fail[now]][i];
            flag[ch[now][i]]|=flag[ch[fail[now]][i]];
        }
    }
}
struct Mat{
    long long mat[111][111];
    Mat(){
        memset(mat,0,sizeof(mat));
    }
};
Mat operator*(const Mat &m1,const Mat &m2){
    Mat m;
    for(int i=0; i<=tn; ++i){
        for(int j=0; j<=tn; ++j){
            for(int k=0; k<=tn; ++k){
                m.mat[i][j]+=m1.mat[i][k]*m2.mat[k][j];
                m.mat[i][j]%=100000;
            }
        }
    }
    return m;
}
int main(){
    idx['A']=0; idx['C']=1; idx['T']=2; idx['G']=3;
    char str[11];
    int m,n;
    while(~scanf("%d%d",&m,&n)){
        tn=0;
        memset(flag,0,sizeof(flag));
        memset(ch,0,sizeof(ch));
        while(m--){
            scanf("%s",str);
            insert(str);
        }
        init();
        Mat e,x;
        for(int i=0; i<=tn; ++i) e.mat[i][i]=1;
        for(int i=0; i<=tn; ++i){
            if(flag[i]) continue;
            for(int j=0; j<4; ++j){
                if(flag[ch[i][j]]) continue;
                ++x.mat[i][ch[i][j]];
            }
        }
        while(n){
            if(n&1) e=e*x;
            x=x*x;
            n>>=1;
        }
        long long res=0;
        for(int i=0; i<=tn; ++i){
            res+=e.mat[0][i];
            res%=100000;
        }
        printf("%lld\n",res);    
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/OscaronMar/article/details/82924250