题意
时间限制:2000ms
单点时限:1000ms
内存限制:256MB
描述
给定字符串,求它的回文子序列个数。回文子序列反转字符顺序后仍然与原序列相同。例如字符串aba中,回文子序列为”a”, “a”, “aa”, “b”, “aba”,共5个。内容相同位置不同的子序列算不同的子序列。
输入
第一行一个整数T,表示数据组数。之后是T组数据,每组数据为一行字符串。
输出
对于每组数据输出一行,格式为”Case #X: Y”,X代表数据编号(从1开始),Y为答案。答案对100007取模。
数据范围
1 ≤ T ≤ 30
小数据
字符串长度 ≤ 25
大数据
字符串长度 ≤ 1000
样例输入
5
aba
abcbaddabcba
12111112351121
ccccccc
fdadfa
样例输出
Case #1: 5
Case #2: 277
Case #3: 1333
Case #4: 127
Case #5: 17
思路
典型的区间dp.
if(s[i] == s[j])
dp[i][j] = dp[i+1][j]+dp[i][j-1]+1;
else
dp[i][j] = dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1];
简单解释一下,先说一下s[i] != s[j]时,dp[i+1][j]+dp[i][j-1]然后再减去重复的dp[i+1][j-1]部分,这个不难理解。
那么当s[i] == s[j]时,显然i与j之间所有是回文串的子序列可以用s[i],s[j]包裹成为新的子序列,所以重复部分不需要剪去了,还需要加上1是因为s[i]s[j]两个单独一起也可以作为一个回文子序列。
代码
#include <cstdio>
#include <iostream>
#include <cstdlib>
#include <cstring>
using namespace std;
const int MOD = 100007;
char s[1009];
int dp[1009][1009];
int main()
{
int T;
scanf("%d", &T);
for(int i=1; i<=T; i++)
{
memset(dp, 0, sizeof(dp));
scanf("%s", s+1);
int n = strlen(s+1);
for(int l=0; l<n; l++)
{
for(int i=1; i+l<=n; i++)
{
int j = i+l;
if(s[i] == s[j])
dp[i][j] = (dp[i+1][j]+dp[i][j-1]+1)%MOD;
else
dp[i][j] = (dp[i+1][j]+dp[i][j-1]-dp[i+1][j-1]+MOD)%MOD;
}
}
printf("Case #%d: %d\n", i, dp[1][n]);
}
return 0;
}