区间dp经典题 HDU 4632 回文子序列计数

HDU 4632

题意:统计所有回文子序列的个数  

这题不同于普通的区间dp 但是也大同小异 省略了一个决策的过程 简单的计数就是 但是这个计数方法也需要一定的思维

dp[l][r] 区间l到区间r的回文子序列个数  

当我们需要求得dp[l][r]的个数时 dp[l][r-1]dp[l+1][r]显然是可以用来转移的 但是我们发现 他们之间有一部分重叠 那就是dp[l+1][r-1] 根据容斥原理 我们需要把这一部分减去  

初步鉴定转移方程是 dp[l][r]=dp[l+1][r]+dp[l][r-1]-dp[l+1][r-1]

但是我们发现答案不正确  那是因为 上述计算的l和r两个位置是分开的  没有把他们的贡献算进去 

那他们有哪些贡献  如果s[l]不等于s[r] 那就不必计算了 因为两者并没有合起来的贡献

如果s[l]等于s[r]的话   首先  字符串s[l]s[r]肯定是一个回文串  那我们可以把答案+1  

然后呢 这就需要严谨的思考  对于区间l+1到r-1的回文子序列是不是左边添上一个s[l] 右边添上一个s[r]可以构成一个新的回文子序列呢 

综上还有一部分就是  if(s[l]==s[r]) dp[l][r]+=dp[l+1][r-1]+1

初始化 dp[i][i]=1 其他为0 表示每一个元区间 都是一个长度为1的回文串

#include<bits/stdc++.h>
using namespace std;
const int N = 1e3+100;
const int mod = 1e4+7;
int dp[N][N];
char s[N];
int main(){
	int t;
	scanf("%d",&t);
	for(int o = 1; o <= t; o++){
		memset(dp,0,sizeof(dp));
		scanf("%s",s+1);
		int len = strlen(s+1);
		for(int i = 1; i <= len; i++) dp[i][i]=1;
		for(int i = 2; i <= len; i++){
			for(int l = 1; l <= len-i+1; l++){
				int r = l+i-1;
				dp[l][r]=(dp[l][r-1]+dp[l+1][r]-dp[l+1][r-1]+mod)%mod;
				if(s[l]==s[r]) dp[l][r]=(dp[l][r]+dp[l+1][r-1]+1)%mod;
			}
		}
		printf("Case %d:",o);
		printf("%d\n",dp[1][len]);
	}
	return 0;
}

 

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值