Palindrome subsequence
题意:有一条字符串让你找出其中不重复的能构成回文串的子序列;
说明一点:子序列并不一定连续!!!一定注意这点!!!
思路: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;
}