URAL 1635. Mnemonics and Palindromes (区间dp)

URAL 1635. Mnemonics and Palindromes(区间dp)

题目链接
题目大意:给定一个字符串,问分割成最少的子串,是每个子串均为回文串。
题目分析:可以用普通的区间dp做法,但是字符串的长度达到了4000,n^3会超时,要想办法降到n^2。想了很久,没有想出来,看了博客的做法。先预处理出来区间[i, j]是否为回文串。假如字符串的长度为10, 那么以6为长度的字符串的最少分割数就等于min(dp[1~5] && is[1~5]。好像说的不太清,看代码吧。

#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;

const int maxn = 4e3 + 100;
const int inf = 0x3f3f3f3f;

int dp[maxn], ans[maxn];
bool is[maxn][maxn], vis[maxn];
char str[maxn];
//dp[i]表示从1~i的最小分割数

int main()
{
    scanf("%s", str + 1);
    int len = strlen(str + 1);

    for(int i = 1; i <= len; i++) {
        is[i][i] = true;
       int l = i - 1, r = i + 1;
       while(1 <= l && r <= len && str[l] == str[r]) {
          is[l][r] = true;
          l--; r++;
       }

       l = i; r = i + 1;
       while(1 <= l && r <= len && str[l] == str[r]) {
           is[l][r] = true;
           l--; r++;
       }
    }


    memset(dp, inf, sizeof(dp));
    memset(ans, 0, sizeof(ans));
    dp[0] = 0;

    for(int i = 1; i <= len; i++) { //枚举长度,因为6这个长度的结果是可以用5推过来的。
        for(int j = 1; j <= i; j++) { //枚举当前长度的断开点
            if(is[j][i] && dp[i] > dp[j - 1] + 1) {
                dp[i] = dp[j - 1] + 1;
                ans[i] = j - 1; //记录断开点
            }
        }
    }

    printf("%d\n", dp[len]);

    int pre = ans[len];
    while(pre) {
        vis[pre] = true;
        pre = ans[pre];
    }

    for(int i = 1; i <= len; i++)
    {
        printf("%c", str[i]);
        if(vis[i]) printf(" ");
    }
    printf("\n");

}

后来的时候,又思考了一下,为什么这个可以用一维来做,之前的取石子问题,括号匹配不能。我认为,是因为这个题dp[i]的状态可以由dp[i - 1]推过来,但是之前的问题都是不可以的。

猜你喜欢

转载自blog.csdn.net/deerly_/article/details/80347603
今日推荐