hdu4632 Palindrome subsequence 回文子序列个数 区间dp Palindrome subsequence

hdu4632 Palindrome subsequence 回文子序列个数 区间dp
Palindrome subsequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 131072/65535 K (Java/Others)
Total Submission(s): 4513    Accepted Submission(s): 1935


Problem Description
In mathematics, a subsequence is a sequence that can be derived from another sequence by deleting some elements without changing the order of the remaining elements. For example, the sequence <A, B, D> is a subsequence of <A, B, C, D, E, F>.
(http://en.wikipedia.org/wiki/Subsequence)

Given a string S, your task is to find out how many different subsequence of S is palindrome. Note that for any two subsequence X = <Sx1, Sx2, ..., Sxk> and Y = <Sy1, Sy2, ..., Syk> , if there exist an integer i (1<=i<=k) such that xi != yi, the subsequence X and Y should be consider different even if Sxi = Syi. Also two subsequences with different length should be considered different.
 
Input
The first line contains only one integer T (T<=50), which is the number of test cases. Each test case contains a string S, the length of S is not greater than 1000 and only contains lowercase letters.
 
Output
For each test case, output the case number first, then output the number of different subsequence of the given string, the answer should be module 10007.
 
Sample Input
4
a
aaaaa
goodafternooneveryone
welcometoooxxourproblems
 
Sample Output
Case 1: 1
Case 2: 31
Case 3: 421
Case 4: 960
 
大致题意是给定一个字符串,求这个字符串的回文子序列个数,最后的答案要%10007
 
题解:用dp[i][j]表示区间[i,j]回文子序列的个数
1、首先,初始化dp[i][j]=1;
 
2、然后预处理子结构,如果s[i]==s[i+1],则dp[i][i+1]=3(a,a,aa),否则 dp[i][i+1]=2  (a,b)
 
3、最后用区间DP处理:
 
如果s[i]==s[j],即子序列首尾相等,那么这段子序列区间内的所有回文子序列都可以和首尾元素再构成新的回文子序列,除此之外 s[i]和s[j]这两个元素也可以构成一个回文子序列
dp[i][j]=(dp[i+1][j]+dp[i][j-1]+1)%mod;
如果s[i]!=s[j],
dp[i][j]=(dp[i+1][j]+dp[i][j-1] dp[i+1][j-1]+mod)%mod;   (容斥原理,中间部分会计算两遍,所以要减去dp[i+1][j-1])
注意:做减法的时候,可能会有负数,为了使%不出现问题,我们需要先加mod再%mod
 

还要注意一下数据类型的问题,这道题目如过把dp[i][j]的类型直接定义为long long 会超时

 

1、因为long long 类型 运算没有int快 所以要改为int型 还有取模的数mod也必须要是int型 如果mod是long long的话也会超时.
 
2、能用int的就不要用long long,因为如果用了long long 就有可能超时;原来一直以为用ll不会爆范围,就总是用ll,现在发现了,一直用ll会爆时间,
尤其是在这种矩阵快速幂的题里,绝对要注意!!!
 
 
#include<iostream>
#include<string.h>
#include<string>
#include<math.h>
#define ll long long
#define mod 10007
using namespace std;
int n,t;
int dp[2005][2005];
char s[1005];
ll min(ll a,ll b)
{
    return a<b?a:b;
}

int main()
{
    cin>>n;
    t=0;
    while(n--)
    {
        t++;
        scanf("%s",s+1);
        int len=strlen(s+1);
        memset(dp,0,sizeof(dp));
        for(int i=1;i<=len;i++)//初始化
            dp[i][i]=1;
        for(int i=1;i<len;i++)//预处理
        {
            if(s[i]==s[i+1])
                dp[i][i+1]=3;//a,a,aa
            else
                dp[i][i+1]=2;//a,b
        }
        for(int l=3;l<=len;l++)
        {
            for(int i=1;i+l-1<=len;i++)
            {
                int j=i+l-1;
                if(j>len)
                    break;
                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
",t,dp[1][len]%mod);
    }
    return 0;

}