[BZOJ3864]Hero meet devil

CXXXIV.[BZOJ3864]Hero meet devil

我们不妨从最trival的LCS问题上想起:暴力的LCS求法是什么?

f(i,j) 表示一个串(不妨设为本题中要填的字符串 T)的前 i 位与另一个串(即题目中给出的 S)的前 j 位所构成的串的LCS。则 f(i,j)=max{f(i,j1),f(i1,j),f(i1,j1)+[ti=sj]}

因为 |S| 很小,所以我们可以考虑设 f(i,S) 表示当前填到 T 的第 i 位,且 S 中通过某种方式储存了 f(i,0)f(i,|S|) 全部的DP值,满足此种情形的串的方案数。下面考虑怎么搞出 S

明显,对于 f(i,0|S|) 来说,相邻两个数的差至多为 1,这一点可以直接从DP式上看出。于是我们可以状压其差分数组,即可实现由数组到状态的一一映射。这之后,我们只需预处理出对于每个 S 对应的状态,其之后添加 A,G,C,T 四个字符会分别到达哪个新状态即可。然后直接DP就行了。

这种手法被称作“DP on DP”,因为DP状态中储存了另一种更简单的DP的数组。可以发现,简单DP的数组压缩后得到了一张自动姬。

时间复杂度 O(m2n)

代码:

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;
int T,n,m,f[2][1<<15],lim,t[20],a[20],b[20],res[20],trans[1<<15][4];
char s[20];
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%s%d",s+1,&m),n=strlen(s+1),lim=1<<n;
		for(int i=1;i<=n;i++){
			if(s[i]=='A')t[i]=0;
			if(s[i]=='G')t[i]=1;
			if(s[i]=='C')t[i]=2;
			if(s[i]=='T')t[i]=3;
		}
		for(int j=0;j<lim;j++){
			for(int k=0;k<n;k++)a[k+1]=a[k]+((j>>k)&1);
			for(int c=0;c<4;c++){
				for(int k=1;k<=n;k++)b[k]=max({a[k-1]+(t[k]==c),a[k],b[k-1]});
				trans[j][c]=0;
				for(int k=0;k<n;k++)trans[j][c]|=(b[k+1]-b[k])<<k;
			}
		}
		memset(f,0,sizeof(f)),f[0][0]=1;
		for(int i=0;i<m;i++){
			memset(f[!(i&1)],0,sizeof(f[!(i&1)]));
			for(int j=0;j<lim;j++)for(int k=0;k<4;k++)(f[!(i&1)][trans[j][k]]+=f[i&1][j])%=mod;
		}
//		for(int i=0;i<=m;i++){for(int j=0;j<lim;j++)printf("%d ",f[i][j]);puts("");}
		for(int i=0;i<lim;i++)(res[__builtin_popcount(i)]+=f[m&1][i])%=mod;
		for(int i=0;i<=n;i++)printf("%d\n",res[i]),res[i]=0;
	}
	return 0;
}

posted @   Troverld  阅读(47)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示