• 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;
    }
  • 相关阅读:
    全排列
    06-联系人管理(xib应用)
    单键模式
    在mac上截屏的几种方式
    05-Tom猫(UIImageView的简单运用)
    04-图片排列切换
    03-图片浏览器(plist的简单应用)
    02-动态创建按钮
    01-实现图片按钮的缩放、动画效果(block的初步应用)
    oracle 监听动态和静态的配置
  • 原文地址:https://www.cnblogs.com/algorithms/p/2628813.html
Copyright © 2020-2023  润新知