POJ 2778 DNA Sequence (自动机DP+矩阵快速幂)

题意:给出m个致病DNA片段,求长为n且不含致病片段的DNA序列共有多少种。

数据范围:0 <= m <= 10,1 <= n <=2000000000

这题初看起来与上一题差不多,但一看数据范围就傻了……

分析:先建m个致病DNA片段的自动机,然后求出自动机中结点之间转移的路径数目,这样就可以得到一个有向图,所求问题就变为从根结点出发,到达任意一点,所走路径长度为n且路径不经过危险结点的路径数目。再抽象一下,就是求一个边长均为1的有向图中2点之间长为n的路径数目,这就不难想到矩阵了。设G为邻接矩阵,则Gk中第i行j列的元素表示从i到j长为k的路径数目。

View Code
#include <stdio.h>

#include <string.h>

#include <queue>

using namespace std;

#define M 11

#define LEN 15

#define SIZE (M*LEN)

#define mod 100000



typedef __int64 LL;



int next[M*LEN][4];

int fail[M*LEN];

bool isend[M*LEN];

int m,n,node;

LL mat[M*LEN][M*LEN];

LL tmp[M*LEN][M*LEN];

LL ans[M*LEN][M*LEN];

void init()

{

    node=1;

    memset(next[0],0,sizeof(next[0]));

}

void add(int cur,int k)

{

    memset(next[node],0,sizeof(next[node]));

    isend[node]=0;

    next[cur][k]=node++;

}

int hash(char c)

{

    switch(c)

    {

        case 'A':   return 0;

        case 'C':   return 1;

        case 'T':   return 2;

        case 'G':   return 3;

    }

}

void insert(char *s)

{

    int i,k,cur;

    for(i=cur=0;s[i];i++)

    {

        k=hash(s[i]);

        if(!next[cur][k])   add(cur,k);

        cur=next[cur][k];

    }

    isend[cur]=1;

}

void build_ac()

{

    queue<int>q;

    int cur,nxt,tmp;



    fail[0]=0;

    q.push(0);



    while(!q.empty())

    {

        cur=q.front(),q.pop();

        for(int k=0;k<4;k++)

        {

            nxt=next[cur][k];

            if(nxt)

            {

                if(cur==0)  fail[nxt]=0;

                else

                {

                    for(tmp=fail[cur];tmp && !next[tmp][k];tmp=next[tmp][k]);

                    fail[nxt]=next[tmp][k];

                }

                if(isend[fail[nxt]]) isend[nxt]=1;

                q.push(nxt);

            }

            else

            {

                next[cur][k]=next[fail[cur]][k];

            }

        }

    }

}

void build_mat()

{

    int cur,nxt,k;

    memset(mat,0,sizeof(mat));

    for(cur=0;cur<node;cur++)

    {

        if(isend[cur])  continue;

        for(k=0;k<4;k++)

        {

            nxt=next[cur][k];

            if(!isend[nxt])  mat[cur][nxt]++;

        }

    }

}

void mat_mul(LL a[][SIZE],LL b[][SIZE])

{

    for(int i=0;i<node;i++)

    {

        for(int j=0;j<node;j++)

        {

            tmp[i][j]=0;

            for(int k=0;k<node;k++)

            {

                tmp[i][j]+=a[i][k]*b[k][j];

            }

            tmp[i][j]%=mod;

        }

    }

}

void mat_pow(int k)

{

    memset(ans,0,sizeof(ans));

    for(int i=0;i<node;i++) ans[i][i]=1;

    while(k)

    {

        if(k&1)

        {

            mat_mul(ans,mat);

            memcpy(ans,tmp,sizeof(tmp));

        }

        k>>=1;

        mat_mul(mat,mat);

        memcpy(mat,tmp,sizeof(tmp));

    }

}

void solve()

{

    build_ac();

    build_mat();

    mat_pow(n);

    LL ret=0;

    for(int i=0;i<node;i++) ret+=ans[0][i];

    printf("%I64d\n",ret%mod);

}

int main()

{

    char s[LEN];

    while(~scanf("%d%d",&m,&n))

    {

        init();

        for(int i=0;i<m;i++)

        {

            scanf("%s",s);

            insert(s);

        }

        solve();

    }

    return 0;

}

 

你可能感兴趣的:(sequence)