题意:给出m个致病DNA片段,求长为n且不含致病片段的DNA序列共有多少种。
数据范围:0 <= m <= 10,1 <= n <=2000000000
这题初看起来与上一题差不多,但一看数据范围就傻了……
分析:先建m个致病DNA片段的自动机,然后求出自动机中结点之间转移的路径数目,这样就可以得到一个有向图,所求问题就变为从根结点出发,到达任意一点,所走路径长度为n且路径不经过危险结点的路径数目。再抽象一下,就是求一个边长均为1的有向图中2点之间长为n的路径数目,这就不难想到矩阵了。设G为邻接矩阵,则Gk中第i行j列的元素表示从i到j长为k的路径数目。
#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; }