LeetCode 132. Palindrome Partitioning II (C++)

题目描述
Given a string s, partition s such that every substring of the partition is a palindrome.
Return the minimum cuts needed for a palindrome partitioning of s.
For example, given s = “aab”,
Return 1 since the palindrome partitioning [“aa”,”b”] could be produced using 1 cut.

解题思路
首先想到的是动态规划,其次想想可否暴力求解。但是动态规划的题用暴力求解通常会超时或者超内存,事实上我在做的时候,复杂度高的动态规划仍然会超时。这道题的测试用例确实很棒,对算法复杂度要求很高。下面先从高复杂的的代码说起,逐步得到“正确”的代码。
解法一(超时):二维动归+遍历

  1. 求回文串:逆序判断等(非动态规划)
  2. 求切割数:s的子串s(i,j): s[i]…s[k-1] | s[k]…s[j]
    从s[k-1]和s[k]之间分割为两部分,则s(i,j)如果不是回文串,则其最小分割的就是找一个合适的k,并在k处分割。这个合适的k需要在i到j直接遍历寻找,这就是动态规划中的遍历操作。递归方程如下:
    f(i,j) = min { f(i,k-1) + f(k,j) } if s(i,j)不是回文
    f(i,j) = 0 if s(i,j)是回文
    上式中,s(i,j)表示s的从i到j的子串,f(i,j)表示子串s(i,j)的最小切割数。注意递归式中等式的左边有两个参数,说明是需要二维动态规划,等式右边有min求最小值,说明需要遍历,此即为二维动归+遍历,代码非常容易写。
  3. 复杂度为O(n^3)
class Solution {
public:
    int minCut(string s) {
    int n=s.length();
    //二维数组,初值设为0,多增加一个空间,以防越界
    vector<vector<int> > f(n+1,vector<int>(n+1,0)); 
    //以子串长度为递归条件
    for(int l=2;l<=n;l++)
    {
        for(int i=0;i<=n-l;i++)
        {
            int j=i+l-1;
            //求字符串是否为回文串
            string s1=s.substr(i,l);//i为起始位置,l为长度
            string s2(s1.rbegin(),s1.rend());//构造逆序串
            if(s1==s2)f[i][j]=0;
            //如果不是回文串则遍历求最小切割数
            else
            {
                f[i][j]=n;//初始化为最大值
                for(int k=i+1;k<=j;k++)//注意作开右闭
                    f[i][j]=min(f[i][j],f[i][k-1]+f[k][j]+1) ;
            }
        }
    }
    return f[0][n-1];//从0开始 
  }
};

解法二(Accept):

  1. 求回文串:动态规划
    s(i,j)为回文串,当且仅当s[i]=s[j],并且s(i+1,j-1)是回文串。用递推方程表示为:d(i,j)=true , if s[i]=s[j]&&s(i+1,j-1)是回文串。

  2. 求切割数:一维动归+备忘录
    备忘录就是提前将s(i,j)是否为回文串记录在一个数组中,供动态规划查找。
    动态规划采用一维+遍历方式,递推方程如下:
    f(j)=min{f(j),f(i-1)+1} ,0<=i<=j,并且s(0,j)不是回文串
    f(j)=0 if s(0,j)是回文串
    f(j)表示s(0,j)的最小切割数。
    s的子串s(0,j) : s[0]…s[i-1] | s[i]…s[j]

  3. 时间复杂度O(n^2).
class Solution {
public:
    int minCut(string s) {
    int n=s.length();
    //回文字串备忘录,记录是子串是否为回文串,初值为false
    vector<vector<bool> > p(n+1,vector<bool>(n+1,false));
    for(int j=0;j<n;j++)
    {
        for(int i=j;i>=0;i--)
            p[i][j]=p[i][j]||(s[i]==s[j]&&(j-i<=1||p[i+1][j-1]));
    }

    vector<int> f(n+1,0);
    for(int i=0;i<n;i++) f[i]=i;
    for(int j=0;j<n;j++)//正序递归
    {
        for(int i=j;i>=0;i--)//逆序递归
        {
            if(p[i][j])
            {
                if(i==0) f[j]=0;
                else f[j]=min(f[j],f[i-1]+1);
            }
        }
    }
    return f[n-1];
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值