LeetCode 132. Palindrome Partitioning II (C++)

来源:互联网 发布:php高并发秒杀 实例 编辑:程序博客网 时间:2024/05/01 04:55

题目描述
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];    }};
0 0