UVa 10617 Again Palindrome(经典回文串区间DP)

题意:

给定一个字符串s,对s进行删除操作,使得剩下的子串是回文字符串,问最多有多少种这种子串。

思路:

涉及到回文字符串,首先要想到的肯定是区间DP,如何写出状态转移方程?

直接从题意切入:dp[i, j]表示区间[i, j]最多有多少个这样的子串。

1. s[i] == s[j] 去掉s[i],则一个子问题就是dp[i+1, j],去掉s[j],另一个子问题就是dp[i, j-1]。

   显然这两个子问题是会有重叠的,比如去掉s[i], s[j]的dp[i+1, j-1]。

   如果s[i],s[j]都不去掉呢?则这个子问题的解显然是dp[i+1, j-1] + 1。

   于是会有 dp[i][j] = dp[i+1][j] + dp[i][j-1] + 1;

2. s[i] != s[j] 此时的子问题就比上述要简单了,因为s[i] s[j]与dp[i+1, j-1]的回文子串构成不了回文串。

   于是 dp[i][j] = dp[i+1][j] + dp[i][j-1] - dp[i+1][j-1];

总结:动态规划变化多端,不同的问题可能会有不同的切入点,一开始切入题目的时候可以采用最直接的按照题意来。

     无果之后再去思考一些见解的方法。这也算是渐进的一种方式了吧,首先不要试图把问题想的太复杂。

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>

#define max(a, b)    (((a) > (b)) ? (a) : (b))
const int MAXN = 65;

char str[MAXN];
long long int dp[MAXN][MAXN];

int main()
{
    int cases;
    scanf("%d", &cases);
    while (cases--)
    {
        scanf("%s", str);
        int n = strlen(str);

        memset(dp, 0, sizeof(dp));

        for (int i = 0; i < n; ++i)
        {
            dp[i][i] = 1;
            if (str[i] == str[i+1])
                dp[i][i+1] = 3;
            else
                dp[i][i+1] = 2;
        }

        for (int p = 2; p < n; ++p)
        {
            for (int i = 0, j = p; j < n; ++i, ++j)
                if (str[i] == str[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];
        }
        printf("%lld\n", dp[0][n-1]);
    }
    return 0;
}

 

posted @ 2012-11-20 16:46  kedebug  阅读(650)  评论(0编辑  收藏  举报