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;

}

 

你可能感兴趣的:(ROM)