hdu 4632区间dp 回文字串计数问题

Palindrome subsequence

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


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
 
有两个点要学习
1. 对于计数类的区间dp 这个状态转移方程比较有意思
dp[i][j] = (dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1] + mod) % mod;
具体理解这个转移方程 自己枚举几个样例就知道了 至于怎么想到这个的
对于s[i]!=s[j]的情况 dp[i][j]所有情况是 dp[i][j-1],dp[i+1][j]这个两个状态的交集 (不得不说一下,由于是枚举所有的情况,这个理解可以从 求i~j这个区间的所有子序列个数归纳出来)
然后当s[i] == s[j]的时候(或者说我们之考虑 dp[i][j-1],dp[i+1][j]两个状态交集的时候),解是不全的 漏下的解是当j 可以和dp[i][j-1]或者 i可以和dp[i+1][j-1]的回文子串构成新的回文字串的情况 这两重情况去重以后 就可以写成
if(a[i] == a[j]) dp[i][j] = (dp[i][j] + dp[i+1][j-1] + 1) % mod;
2.就是。。 取模的时候 如果有减法。 一定要加上模数啊。。 负数wa死你
#include<cstdio>
#include<iostream>
#include<cstring>

using namespace std;
const int maxn = 1005;
const int mod = 10007;

char a[maxn];
int n,dp[maxn][maxn];

int solve(){
    memset(dp,0,sizeof(dp));
    for(int i=0;i<n;i++) dp[i][i]=1;
    for(int i = n-2 ; i >= 0 ;i--)// 由于dp[i][j]依赖 dp[i+1] 所以枚举区间的时候,我们要从大到小枚举i
    {
        for(int j = i+1 ;j < n ; j++)
       {
            dp[i][j] = (dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1] + mod) % mod;
            if(a[i] == a[j]) dp[i][j] = (dp[i][j] + dp[i+1][j-1] + 1) % mod;
        }
    }
    return dp[0][n-1];
}

int main(){
    int cas;
    scanf("%d",&cas);
    for(int T = 1 ; T <= cas; T++){
        scanf("%s",a);
        n = strlen(a);
        printf("Case %d: %d\n",T,solve());
    }

    return 0;
}

  

 
posted @ 2017-04-29 16:09  猪突猛进!!!  阅读(200)  评论(0编辑  收藏  举报