zoukankan      html  css  js  c++  java
  • 【BZOJ3864】Hero meet devil DP套DP

    【BZOJ3864】Hero meet devil

    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

    题意:给你一个串S,问所有长度为m的字符串中,与S串的最长公共子序列长度为1...|S|的串的个数。

    题解:话说这种DP套DP的题最近有点流行~

    还记得怎么求最长公共子序列吗?记得那个求最长公共子序列时的矩阵吗?不记得我就再说一遍。

    令f[i][j]表示T串中到了第i个数,S串中到了第j个数,的LCS的长度。那么经典的DP方程:

    $f[i][j]=max(f[i-1][j],f[i][j-1],(T[i]==S[j])?(f[i-1][j-1]+1):0)$

    好了,但是我们求的是方案数,如果直接这样DP的话,需要记录的状态非常多(当前T可能的字符,之前T可能的字符。。。)。但是我们发现S的长度非常小,可以考虑把它单独拿出来处理一下。

    因为每一行只能从上一行转移过来,我们不妨状压所有可能的行,暴力计算出在T中添加一个字符后会转移到哪个行。但是行中每一位的数不是0/1,差分一下就好了。处理出所有的转移后,再跑一个DP统计答案就行了。

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cstring>
    using namespace std;
    const int mod=1000000007;
    int n,m;
    int to[1<<16][4],s[20],cnt[1<<16],f[2][1<<16],ans[20];
    char str[20];
    void init()
    {
    	memset(to,0,sizeof(to));
    	memset(cnt,0,sizeof(cnt));
    	memset(f,0,sizeof(f));
    	memset(ans,0,sizeof(ans));
    	int i,j,k,s1,s2,t1,t2,tar;
    	for(i=0;i<(1<<n);i++)
    	{
    		if(i)	cnt[i]=cnt[i-(i&-i)]+1;
    		for(j=0;j<4;j++)
    		{
    			for(tar=s1=s2=t1=t2=0,k=0;k<n;k++)
    			{
    				t1=s1,t2=s2,s2+=((i>>k)&1),s1=max(t1,s2);
    				if(s[k]==j)	s1=max(s1,t2+1);
    				tar|=((s1-t1)<<k);
    			}
    			to[i][j]=tar;
    		}
    	}
    }
    void work()
    {
    	scanf("%s%d",str,&m),n=strlen(str);
    	int i,j;
    	for(i=0;i<n;i++)
    	{
    		if(str[i]=='A')	s[i]=0;
    		if(str[i]=='G')	s[i]=1;
    		if(str[i]=='C')	s[i]=2;
    		if(str[i]=='T')	s[i]=3;
    	}
    	init();
    	f[0][0]=1;
    	for(j=0;j<=m;j++)
    	{
    		for(i=0;i<(1<<n);i++)	f[(j&1)^1][i]=0;	
    		for(i=0;i<(1<<n);i++)	f[(j&1^1)][to[i][0]]=(f[j&1^1][to[i][0]]+f[j&1][i])%mod,f[(j&1^1)][to[i][1]]=(f[j&1^1][to[i][1]]+f[j&1][i])%mod,f[(j&1^1)][to[i][2]]=(f[j&1^1][to[i][2]]+f[j&1][i])%mod,f[(j&1^1)][to[i][3]]=(f[j&1^1][to[i][3]]+f[j&1][i])%mod;
    	}
    	for(i=0;i<(1<<n);i++)	ans[cnt[i]]=(ans[cnt[i]]+f[m&1][i])%mod;
    	for(i=0;i<=n;i++)	printf("%d
    ",ans[i]);
    }
    int main()
    {
    	int T;
    	scanf("%d",&T);
    	while(T--)	work();
    	return 0;
    }

     

  • 相关阅读:
    字集码(字符编码)
    图片轮播(可实现手动与自动的切换)
    Eclipse常用快捷键
    Java并发编程:Callable、Future和FutureTask
    Java并发之CountDownLatch、CyclicBarrier和Semaphore
    java注解
    JVM加载class原理
    阿里中间件技术及双十一实践--软负载——分布式系统的引路人
    阿里中间件技术及双十一实践--中间件总体介绍
    Java的LockSupport.park()实现分析
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/7114724.html
Copyright © 2011-2022 走看看