Palindrome subsequence HDU - 4632 (区间DP)

Palindrome subsequence

 题目链接:HDU - 4632 

题意:有一条字符串让你找出其中不重复的能构成回文串的子序列;

说明一点:子序列并不一定连续!!!一定注意这点!!!

思路:dp[i][j]表示区间[i, j]中的回文串数量,那么:

dp[i][j]=dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+(s[i]==s[j]?(dp[i+1][j-1]+1):0)    ????????

这个式子感觉好突兀有没有??我也是被这个式子整的一脸懵逼~~~奈何终究没找到一篇博客解释这个式子的,,,(来自菜鸡的无奈,大牛可以略过下面两段,不过真正的大牛应该是不会看这篇文章是了,,,)(PS:上式我将两个式子合到一起了,因为我感觉这样比分开更好理解)

先不管式子的前半部分,先看一下(s[i]==s[j]?(dp[i+1][j-1]+1):0) 这个式子;如果s[i]==s[j]那么{s[i], s[j]}这个序列一定是回文无疑了,所以加了个1,然后再加上中间的部分能构成几个回文呢?已知dp[i+1][j-1]是区间[i+1, j-1]的回文串数量,那么在这个区间的回文串一定可以和s[i], s[j]构成新的回文串,也就是一共dp[i+1][j-1]个;所以说,如果s[i]==s[j],就新增了dp[i+1][j-1]+1个回文串;那问题又来了,原先共有多少回文串呢???这就是上式前半部分的作用了;

现在看看前半部分,其实就是两句话说完:区间[i, j-1]和[i+1, j]的回文串之和,又区间[i+1, j-1]加重了,所以再减去区间[i+1, j-1]的回文串;没了???没了!!!好吧,这只是我看着式子理解的,那这是怎么推出来的呢???

[i, j]区间的回文串数量必定是[i, j]中所有区间的回文串数量之和,那么就有下面式子:

dp[i][j]=dp[i][i]+dp[i][i+1]+dp[i][[i+2]+···+dp[i][j-1]+dp[i][j]+dp[i+1][i+1]+dp[i+1][i+2]+···+dp[i+1][j-1]+dp[i+1][j]+······+dp[j][j]-重合部分;

重合部分=(j-i)*(dp[i][i]+dp[j][j])+(j-i-1)*(dp[i+1][i+1]+dp[j-1][j-1])+(j-i-2)*(dp[i+2][i+2]+dp[j-2][j-2])+···+0*(dp[(i+j)/2][(i+j)/2]);

化简后其实就是dp[i][j-1]+dp[i+1][j]-dp[i+1][j-1];(化简过程????没有!!!实在是无能为力了~~~)

或者可以把这个式子看作是递归貌似更好理解?

反正我的水平就这点儿~~~直接呈上代码了:

#include <bits/stdc++.h>
using namespace std;
char s[1100];
const int mod=10007;
int dp[1100][1100];
int main(){
	int T, cas=0;
	scanf("%d", &T);
	while(T--){
		scanf("%s", s);
		int len=strlen(s);
		memset(dp, 0, sizeof(dp));
		for(int i=0; i<len; i++){
			dp[i][i]=1;
		}
		for(int l=2; l<=len; l++){
			for(int i=0; l+i<=len; i++){
				int j=l+i-1;
				dp[i][j]=(dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+(s[i]==s[j]?(dp[i][j]+dp[i+1][j-1]+1):0)+mod)%mod;
			}
		}
		printf("Case %d: %d\n", ++cas, (dp[0][len-1]+mod)%mod);//注意取模部分!!!!因为可能被减成负数!!!
	}
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值