时间限制:
2000ms
单点时限:
1000ms
内存限制:
256MB
-
5 aba abcbaddabcba 12111112351121 ccccccc fdadfa
样例输出
-
Case #1: 5 Case #2: 277 Case #3: 1333 Case #4: 127 Case #5: 17
描述
给定字符串,求它的回文子序列个数。回文子序列反转字符顺序后仍然与原序列相同。例如字符串aba中,回文子序列为"a", "a", "aa", "b", "aba",共5个。内容相同位置不同的子序列算不同的子序列。
输入
第一行一个整数T,表示数据组数。之后是T组数据,每组数据为一行字符串。
输出
对于每组数据输出一行,格式为"Case #X: Y",X代表数据编号(从1开始),Y为答案。答案对100007取模。
数据范围
1 ≤ T ≤ 30
小数据
字符串长度 ≤ 25
大数据
字符串长度 ≤ 1000
题解:用dp[i][j] 表示以i为第一个字符,j为最后一个字符的回文字符序列的个数。转移就是:
dp[i][j]+=dp[x][y] ,(i<x<j,i<y<j)
直接写复杂度是n^4的,我们可以看出sum(dp[x][y] (i<x<j,i<y<j))是dp数组里面一个矩形区域的和。因此我们可以用sum[i][j]记录以dp[i][j]为左上角,以dp[n][n]为右下角的矩形区域的和,那么可以O(1)实现转移。复杂度O(n^2)。
代码如下:
#include<stdio.h>
#include<iostream>
#include<string>
#include<map>
#include<string.h>
#define nn 1100
#define mod 100007
typedef long long LL;
using namespace std;
char s[nn];
LL dp[nn][nn];
LL sum[nn][nn];
int main()
{
int t,i,j;
int cas=1;
scanf("%d",&t);
while(t--)
{
scanf("%s",s);
memset(dp,0,sizeof(dp));
memset(sum,0,sizeof(sum));
int n=strlen(s);
int x,y;
for(i=n-1;i>=0;i--)
{
for(j=n-1;j>=0;j--)
{
if(j>=i&&s[i]==s[j])
{
x=i+1;
y=j-1;
if(x>y)
{
dp[i][j]=1;
}
else
dp[i][j]=((sum[x][x]-sum[x][y+1]-sum[y+1][x]+sum[y+1][y+1])%mod+1+mod)%mod;
}
sum[i][j]=((sum[i+1][j]+sum[i][j+1]-sum[i+1][j+1]+dp[i][j])%mod+mod)%mod;
}
}
printf("Case #%d: %lld\n",cas++,sum[0][0]);
}
return 0;
}