1 GTC 10
1 22783 528340 497452
题意:给一个字符串s长度小于15,求长度为n的所有字符串(AGTC组成)与s公共子序列长度为0,1,2..s.lengt()的个数,
clj的题解没看懂,jl讲了一遍没太懂,然后问了cs,回去想了下还是没想明白,再问了 一次才搞懂。。。
解题思想:
dp[i][j] 表示s前j个字符与一个串(假设为t)前i个字符得到的最长公共子序列。
根据转移公式 dp[i][j] = max(dp[i-1][j],dp[i-1][j-1]+1) 根据转移公式,其实只要知道dp[i-1]的信息就可以了,小于i-1的信息不需要了
不妨去掉i这个维度,dp[j]表示与当前长度与s得到的最长公共子序列
那么dp[j]是一个不降的序列-------显然,因为如果求的串是s0->sj与t的公共子序列,s0->sj肯定与t所求结果要大于等于s0->sj-1
接下来构造转移方程:以dp作为一个状态,x=(dp[0],dp[1],....,dp[|s|])构成一个新的状态记为res[x]
举个例子: x表示状态,就是dp[j]的值,表示为用前j个字符求得最大公共子序列数A,G,T,C表示添加一个字符,这些字符的下方为添加该字符以后得到的新状态
GTC 表示s串
x A G T C
000 000 111 011 001
001 001 111 011 001
011 011 111 011 012
111 111 111 122 112
112 112 112 122 122
122 122 122 122 123
123 123 123 123 123
有点多,总共的状态为2^|s| 转移数为4
当x等于122时说明当前匹配的结果为GT,添加一个C的话就可以匹配成功GTC了,添加其他字符没啥影响的,不增加dp[j]的值
现在用这些状态,进行压缩处理
x=(dp[0],dp[1],....,dp[|s|])
如果dp[i] == dp[i-1] +1那么i位置置1否则为0
那么x就可以变成一个二进制表示的状态了,然后1的个数就表示为可以匹配到的最长的公共子序列的值,将最后的结果,包含1
的个数相同的状态合并,然后就是最后的结果了
转移方程和上面举例的表格一致。
当然,其实很明显,并不是所有的状态都是可达的,
如果直接2*|S|*n*4的话挺费时间的,虽然不超时,但是优化一下会更好些
把有效状态提出来,每次只对有效的状态进行转移可以快3倍吧。。一下贴两种方式的代码
不优化 5.5s左右
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 1<<16 #define ll long long #define mod 1000000007 ll dp[2][maxn]; int con[maxn][5]; int ID[200]; int value[20]; char word[20]; void Dostate(int len){ static int res[30]; memset(res,0,sizeof(res)); memset(con,0,sizeof(con)); int n = 1<<len; int i,j,k,s; for( i = 0;i < n; i++){ for(j = 0;j < 4;j++){ for( k = 0;k < len; k++){ if((1<<k)&i)res[k+1]=1; else res[k+1] = 0; } for(k=1;k<=len;k++)res[k]+=res[k-1]; for(k=len;k>0;k--){ if(value[k] == j) res [k] = max(res[k],res[k-1]+1); } for(k=1;k<=len;k++)res[k] = max(res[k],res[k-1]); for(k=1,s=0;k<=len;k++) if(res[k]>res[k-1])s |= (1<<k-1); con[i][j] = s; } } } void add(ll &a,ll b){ a+=b; if(a >= mod) a-=mod; } int count(int i){ int ans = 0; while(i){ if(i&1)ans++; i/=2; } return ans; } void work(int n,int len,int s){ int p=0,q=1; memset(dp[0],0,sizeof(dp[0])); dp[0][0] = 1; int i,j,k; for( i = 0;i < n; i++){ memset(dp[q],0,sizeof(dp[q])); for( j = 0;j < s; j++){ for(k=0;k<4;k++){ add(dp[q][con[j][k]],dp[p][j]); } } swap(p,q); } static ll ans[30]; memset(ans,0,sizeof(ans)); for( i = 0;i < s; i++){ add(ans[count(i)],dp[p][i]); } for(i=0;i<= len;i++) cout<<ans[i]<<endl; } int main(){ ID['A'] = 0,ID['G'] = 1; ID['T'] = 2,ID['C'] = 3; int t,n; scanf("%d",&t); while(t--){ scanf("%s",word); scanf("%d",&n); int len = strlen(word); for(int i = 0;i < len; i++) value[i+1] = ID[word[i]]; Dostate(len); work(n,len,1<<len); } return 0; }
优化后 1.7
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 1<<15 #define ll int #define mod 1000000007 ll dp[2][maxn]; int con[maxn][5]; int ID[200]; int value[20]; char word[20]; int queue[maxn]; int check[maxn]; int Map(int s){ memset(check,-1,sizeof(check)); int front = 1,rear=0; queue[0] = 0; check[0] = 0; int u,i,v; while(front!=rear){ u = queue[rear++]; for(i=0;i<4;i++){ v = con[u][i]; if(check[v] != -1) continue; check[v] = front; queue[front++] = v; } } return front; } void Dostate(int len){ static int res[30]; memset(res,0,sizeof(res)); memset(con,0,sizeof(con)); int n = 1<<len; int i,j,k,s; for( i = 0;i < n; i++){ for(j = 0;j < 4;j++){ for( k = 0;k < len; k++){ if((1<<k)&i)res[k+1]=1; else res[k+1] = 0; } for(k=1;k<=len;k++)res[k]+=res[k-1]; for(k=len;k>0;k--){ if(value[k] == j) res [k] = max(res[k],res[k-1]+1); } for(k=1;k<=len;k++)res[k] = max(res[k],res[k-1]); for(k=1,s=0;k<=len;k++) if(res[k]>res[k-1])s |= (1<<k-1); con[i][j] = s; } } } void add(ll &a,ll b){ a+=b; if(a >= mod) a-=mod; } int count(int i){ int ans = 0; while(i){ if(i&1)ans++; i/=2; } return ans; } void work(int n,int len,int s){ int p=0,q=1; memset(dp[0],0,sizeof(dp[0])); dp[0][0] = 1; int i,j,k,u,v; s = Map(s); for( i = 0;i < n; i++){ //memset(dp[q],0,sizeof(dp[q])); for(j=0;j<s;j++)dp[q][j]=0; for( j = 0;j < s; j++){ u=queue[j]; for(k=0;k<4;k++){ v=check[con[u][k]]; add(dp[q][v],dp[p][j]); } } swap(p,q); } static ll ans[30]; memset(ans,0,sizeof(ans)); for( i = 0;i < s; i++){ add(ans[count(queue[i])],dp[p][i]); } for(i=0;i<= len;i++) cout<<ans[i]<<endl; } int main(){ ID['A'] = 0,ID['G'] = 1; ID['T'] = 2,ID['C'] = 3; int t,n; scanf("%d",&t); while(t--){ scanf("%s",word); scanf("%d",&n); int len = strlen(word); for(int i = 0;i < len; i++) value[i+1] = ID[word[i]]; Dostate(len); work(n,len,1<<len); } return 0; }