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

DNA Sequence
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 18472   Accepted: 7109

Description

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

Source



        大致题意,与之前那道题目差不多,也是由指定的一些字母构成的字符串,不能够包换一些不合法的字符,再告诉你一个长度,问你一共有多少个合法的字符串。但不同的是,这道题目的长度可以很长。

        上一道题目,我们介绍了用dp的方法去解决,但是这题面对这么长的长度,显然是不可取的。但是我们回顾一下上一道题目dp的过程,dp[i][j]表示到达AC自动机上的j点走了i步时的方案数,最后要求的是 Σdp[n][j],即到达每个合法点走了n步时的方案数总和。而在图论中,邻接矩阵的乘法、次方,恰好可以表示一个人在图中走了确定的某个步数之后的方案数。所以说对于这题,我们可以把AC自动机想象成一个有向图,然后在这个有向图上面走长度那么多的步数。AC自动机上的连边就是这个有向图中的连边,然后对于那些不合法单词,我们还是一样把末尾节点特殊标记为不可达,因为根据AC自动机的性质,能够到达这个点就一定能够构成至少一个不合法单词。构造出矩阵之后,直接矩阵快速幂即可。

        具体见代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
#define INF 0x3f3f3f3f
#define LL long long
#define mod 100000
#define N 105

using namespace std;

int h[256],n,m,p;

struct AC_automation
{

    struct node{int fail,cnt,ch[4];} T[110];
    int tot,root;

    void init()
    {
        tot=root=0;
        memset(T,0,sizeof(T));
    }

    void ins(char* x)
    {
        int o=root;
        for(int k=0;x[k];k++)
        {
            int c=h[x[k]];
            if(!T[o].ch[c]) T[o].ch[c]=++tot;
            o=T[o].ch[c];
        }
        T[o].cnt++;
    }

    void get_fail()
    {
        queue<int> q;
        q.push(root);
        while(!q.empty())
        {
            int o=q.front();
            T[o].cnt=T[o].cnt|T[T[o].fail].cnt;
            for(int i=0;i<4;i++)
            {
                if (!T[o].ch[i])
                {
                    T[o].ch[i]=T[T[o].fail].ch[i];
                    continue;
                }
                if (o!=root)
                {
                    int fa=T[o].fail;
                    while(fa&&!T[fa].ch[i]) fa=T[fa].fail;
                    T[T[o].ch[i]].fail=T[fa].ch[i];
                } else T[T[o].ch[i]].fail=root;
                q.push(T[o].ch[i]);
            } q.pop();
        }
    }

} AC;

struct matrix
{
    LL a[N][N];
	void init(int x){memset(a,0,sizeof(a));}

	friend matrix operator *(matrix x,matrix y)
    {
       	matrix ans;
       	for(int i=0;i<=p;i++)
            for(int j=0;j<=p;j++)
            {
                ans.a[i][j]=0;
                for(int k=0;k<=p;k++)
                    ans.a[i][j]+=x.a[i][k]*y.a[k][j]%mod;
                ans.a[i][j]%=mod;
            }
       	return ans;
   	}

    friend matrix operator ^(matrix x,LL y)
    {
        matrix ans;
        if (y==0)
        {
            memset(ans.a,0,sizeof(ans.a));
            for(int i=1;i<=p;i++) ans.a[i][i]=1;
            return ans;
        } else while ((y&1)==0) y>>=1,x=x*x;
        ans=x; y>>=1;
        for(;y!=0;y>>=1)
        {
            x=x*x; if ((y&1)!=0) ans=ans*x;
        }
        return ans;
    }
} x;


int main()
{
    h['A']=0;h['T']=1;
    h['C']=2;h['G']=3;
    while(~scanf("%d%d",&n,&m))
    {
        AC.init();char s[15];
        for(int i=1;i<=n;i++)
        {
            scanf("%s",s); AC.ins(s);
        }
        p=AC.tot;
        AC.get_fail();
        x.init(AC.tot);
        for(int i=0;i<=AC.tot;i++)
            if (!AC.T[i].cnt)
            for(int j=0;j<4;j++)
            {
                int k=AC.T[i].ch[j];
                if (!AC.T[k].cnt) x.a[i][k]++;
            }
        x=x^m; int ans=0;
        for(int i=0;i<=AC.tot;i++)
            ans=(ans+x.a[0][i])%mod;
        cout<<ans<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值