hdu 4899 Hero meet devil 2014多校联合训练赛 状态压缩dp 最长公共子序列


Hero meet devil

Time Limit: 16000/8000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 131    Accepted Submission(s): 57


Problem Description
There is an old country and the king fell in love with a devil. The devil always asks the king to do some crazy things. Although the king used to be wise and beloved by his people. Now he is just like a boy in love and can’t refuse any request from the devil. Also, this devil is looking like a very cute Loli.

After the ring has been destroyed, the devil doesn't feel angry, and she is attracted by z*p's wisdom and handsomeness. So she wants to find z*p out.

But what she only knows is one part of z*p's DNA sequence S leaving on the broken ring.

Let us denote one man's DNA sequence as a string consist of letters from ACGT. The similarity of two string S and T is the maximum common subsequence of them, denote by LCS(S,T).

After some days, the devil finds that. The kingdom's people's DNA sequence is pairwise different, and each is of length m. And there are 4^m people in the kingdom.

Then the devil wants to know, for each 0 <= i <= |S|, how many people in this kingdom having DNA sequence T such that LCS(S,T) = i.

You only to tell her the result modulo 10^9+7.
 

Input
The first line contains an integer T, denoting the number of the test cases.
For each test case, the first line contains a string S. the second line contains an integer m.

T<=5
|S|<=15. m<= 1000.
 

Output
For each case, output the results for i=0,1,...,|S|, each on a single line.
 

Sample Input
  
  
1 GTC 10
 

Sample Output
  
  
1 22783 528340 497452
 

Author
WJMZBMR
 

Source

题意:给一个字符串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;
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值