[bzoj3864]Hero meet devil

又是一道神题,看题解+看代码,三个小时终于会了QAQ。
在这里插入图片描述

  • dp套dp的经典题目。
  • 什么问题是dp套dp问题?这类问题让你求解的是有多少种方案使得一个dp的最终值为一个特定值。对应到本题来说,是让你求解有多少种长度为m的字符串,与S串的 L C S LCS LCS i i i
  • 这类问题,我们一般要观察原本dp的求解过程与自身性质,把内dp尝试压成状态,递推外dp值。
  • 考虑 L C S LCS LCS的求解过程。
    f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j ] , f [ i ] [ j − 1 ] ) f[i][j]=max (f[i-1][j],f[i][j-1]) f[i][j]=max(f[i1][j],f[i][j1])
    i f ( s [ i ] = = t [ j ] )     f [ i ] [ j ] = m a x ( f [ i − 1 ] [ j − 1 ] + 1 ) if(s[i]==t[j])~~~f[i][j]=max(f[i-1][j-1]+1) if(s[i]==t[j])   f[i][j]=max(f[i1][j1]+1)
  • 当T串确定的时候, f [ i ] [ j ] , 1 &lt; = i &lt; = n f[i][j],1&lt;=i&lt;=n f[i][j],1<=i<=n,这个值是单调不减的。并且相邻两个的差值最大为1。因此,我们可以把f数组差分,压成二进制数。然后每一个状态(即二进制数)对应一个T串,假如一个新的字符假如T串,它与S的 L C S LCS LCS匹配形成的新状态,我们是可以预处理出来的。
  • 现在我们可以跑dp了。 d p [ i ] [ j ] dp[i][j] dp[i][j]表示到了第i个字符,匹配状态为 j j j的方案数。那么 d p [ i + 1 ] [ t r [ j ] [ k ] ] + = d p [ i ] [ j ] dp[i+1][tr[j][k]]+=dp[i][j] dp[i+1][tr[j][k]]+=dp[i][j],其中 t r [ j ] [ k ] tr[j][k] tr[j][k]代表 j j j代表的串加进 k k k字符后形成的新状态。
  • okk,终于做完了。记得多组数据的时候,数组一定不要忘了memset!今天第二次因为这个挂掉了233.
Coding
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e3+1;
const int mod=1e9+7;
const int M=1<<15;
const int maxn=15;
int n,m,t,a[maxn+2],tran[M][5],f[N+2][M+2],tf[2][17],ans[17];
char s[maxn+1];char ch[5]={'a','A','C','T','G'};
int change(char c){
	for(int i=1;i<=4;++i) if(c==ch[i]) return i;
	return 5;
}
int trans(int v,int c){
	memset(tf,0,sizeof(tf));
	for(int i=0;i<n;++i) tf[0][i+1]=(v>>i)&1;
	for(int i=1;i<=n;++i) tf[0][i]+=tf[0][i-1];
	for(int i=1;i<=n;++i){
		int tmp=0;
		tf[1][i]=max(tf[0][i],tf[1][i-1]);
		if(a[i]==c)
			tf[1][i]=max(tf[1][i],tf[0][i-1]+1);
	}
	int res=0;
	for(int i=0;i<n;++i) res+=(1<<i)*(tf[1][i+1]-tf[1][i]);
	return res;
}
int count(int v){
	int res=0;
	for(int i=0;i<maxn;++i) res+=((v>>i)&1);
	return res;
}
int main(){
	scanf("%d",&t);
	while(t--){
		memset(tran,0,sizeof(tran));
		memset(ans,0,sizeof(ans));
		memset(f,0,sizeof(f));
		scanf("%s",s+1);
		n=strlen(s+1);
		scanf("%d",&m);
		for(int i=1;i<=n;++i) a[i]=change(s[i]);
		for(int i=0;i<(1<<n);++i) for(int j=1;j<=4;++j) tran[i][j]=trans(i,j);
		f[0][0]=1;
		for(int i=1;i<=m;++i){
			for(int j=0;j<(1<<n);++j){
				for(int k=1;k<=4;++k){
					int x=tran[j][k];
					f[i][x]=(f[i][x]+f[i-1][j])%mod;
				}
			}
		}
		for(int i=0;i<(1<<n);++i) ans[count(i)]=(ans[count(i)]+f[m][i])%mod;
		for(int i=0;i<=n;++i) printf("%d\n",ans[i]);
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值