【POJ2778】DNA Sequence 【AC自动机,dp,矩阵快速幂】
题意
题目给出m(m<=10)个仅仅由A,T,C,G组成的单词(单词长度不超过10),然后给出一个整数n(n<=2000000000),问你用这四个字母组成一个长度为n的长文本,有多少种组成方法可以使得它不含任何一个给出的单词。
分析
当时一看以为是跟训练指南上(UVA11468)一样的题,感觉只有四个字母并且单词数量和长度也比较小,但是一看给出的n有点懵逼。如果再按照书上建立AC自动机以后直接跑DP的方法肯定是不行了。然后我们就要用到,递推利器,矩阵快速幂。
我们还是按照套路先把AC自动机建出来,然后将每个单词结点设为非法结点,题目变成在AC自动机中走n步不通过非法结点的方案数。然后设f[i][j]是当前在结点i,已经走了j步,且未走过非法结点的方案数。然后怎么转移呢?
f[i][j]=A(0,i)*f[0][j-1]+A(1,i)*f[1][j-1]+...+A(sz-1,i)f[sz-1][j-1]。其中A(i,j)的含义就是从i到j有几条直接连接的边。那么将这个dp方程拆开来看
f0[i]=A(0,0)*f[0][i-1]+A(1,0)*f[1][i-1]+....+A(sz-1,0)*fsz-1[i-1]
f1[i]=A(0,1)*f[0][i-1]+A(1,1)*f[1][i-1]+....+A(sz-1,1)*fsz-1[i-1]
.
.
fsz-1[i]=A(0,sz-1)*f[0][i-1]+A(1,sz-1)*f[1][i-1]+...+A(sz-1,sz-1)*fsz-1[i-1]
然后根据这个我们就很好建立转移矩阵
*
(第一次学会用公式编辑器不过好像还是贼丑)
然后建立这个大的转移矩阵,矩阵的(i,j)为结点i到结点j的直接路径的条数,然后跑一个矩阵快速幂。
最后把从0到sz-1结点的f(n)的值全部加起来就是答案了。
对了这个题需要用long long
下面是代码
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <iostream> 5 #include <queue> 6 #include <map> 7 8 using namespace std; 9 const int maxnode=1100; 10 const int MOD=100000; 11 map<char,int>M; 12 struct AC_Automata{ 13 int ch[maxnode][4],f[maxnode],last[maxnode],val[maxnode],match[maxnode]; 14 int sz; 15 int idx(char c){ 16 return M[c]; 17 } 18 void init(){ 19 sz=1; 20 memset(ch[0],0,sizeof(ch[0])); 21 memset(match,0,sizeof(match)); 22 val[0]=0; 23 } 24 void insert(char *s){ 25 int n=strlen(s),u=0; 26 for(int i=0;i<n;i++){ 27 int c=idx(s[i]); 28 if(!ch[u][c]){ 29 ch[u][c]=sz; 30 memset(ch[sz],0,sizeof(ch[sz])); 31 val[sz++]=0; 32 } 33 u=ch[u][c]; 34 } 35 val[u]=1; 36 match[u]=1; 37 } 38 void getFail(){ 39 queue<int>q; 40 last[0]=f[0]=0; 41 for(int i=0;i<4;i++){ 42 int u=ch[0][i]; 43 if(u){ 44 q.push(u); 45 f[u]=last[u]=0; 46 } 47 } 48 while(!q.empty()){ 49 int r=q.front();q.pop(); 50 for(int i=0;i<4;i++){ 51 int u=ch[r][i]; 52 if(!u){ 53 ch[r][i]=ch[f[r]][i]; 54 continue; 55 } 56 q.push(u); 57 int v=f[r]; 58 while(v&&!ch[v][i])v=f[v]; 59 f[u]=ch[v][i]; 60 match[u]|=match[f[u]]; 61 } 62 } 63 } 64 }ac; 65 const int maxN=101; 66 struct Matrix{ 67 long long a[maxN][maxN]; 68 void init(){ 69 memset(a,0,sizeof(a)); 70 for(int i=0;i<ac.sz;i++) 71 a[i][i]=1; 72 } 73 }; 74 Matrix mul(Matrix a,Matrix b){ 75 Matrix res; 76 for(int i=0;i<ac.sz;i++){ 77 for(int j=0;j<ac.sz;j++){ 78 res.a[i][j]=0; 79 for(int k=0;k<ac.sz;k++){ 80 res.a[i][j]+=a.a[i][k]*b.a[k][j]; 81 res.a[i][j]%=MOD; 82 } 83 } 84 } 85 return res; 86 } 87 Matrix qpow(Matrix a,int k){ 88 Matrix res; 89 res.init(); 90 while(k){ 91 if(k%2)res=mul(res,a); 92 a=mul(a,a); 93 k/=2; 94 } 95 return res; 96 } 97 int n,m; 98 char s[30]; 99 100 int main(){ 101 M['A']=0,M['C']=1,M['T']=2,M['G']=3; 102 ac.init(); 103 scanf("%d%d",&m,&n); 104 for(int i=1;i<=m;i++){ 105 scanf("%s",s); 106 ac.insert(s); 107 } 108 ac.getFail(); 109 Matrix A; 110 for(int i=0;i<ac.sz;i++){ 111 if(!ac.match[i]) 112 for(int j=0;j<4;j++){ 113 int u=ac.ch[i][j]; 114 if(!ac.match[u]) 115 A.a[i][u]++; 116 } 117 } 118 /*for(int i=0;i<ac.sz;i++){ 119 for(int j=0;j<ac.sz;j++){ 120 printf("%d ",A.a[i][j]); 121 } 122 printf("\n"); 123 }*/ 124 125 Matrix S; 126 S.a[0][0]=1; 127 Matrix ANS; 128 ANS=qpow(A,n); 129 ANS=mul(S,ANS); 130 long long ans=0; 131 for(int i=0;i<=ac.sz;i++) 132 ans+=ANS.a[0][i]; 133 printf("%d\n",ans%MOD); 134 return 0; 135 }