LeetCode 132. Palindrome Partitioning II (C++)
2016-01-25 23:36
375 查看
题目描述
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.
解题思路
首先想到的是动态规划,其次想想可否暴力求解。但是动态规划的题用暴力求解通常会超时或者超内存,事实上我在做的时候,复杂度高的动态规划仍然会超时。这道题的测试用例确实很棒,对算法复杂度要求很高。下面先从高复杂的的代码说起,逐步得到“正确”的代码。
解法一(超时):二维动归+遍历
求回文串:逆序判断等(非动态规划)
求切割数: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求最小值,说明需要遍历,此即为二维动归+遍历,代码非常容易写。
复杂度为O(n^3)
解法二(Accept):
求回文串:动态规划
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)是回文串。
求切割数:一维动归+备忘录
备忘录就是提前将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]
时间复杂度O(n^2).
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.
解题思路
首先想到的是动态规划,其次想想可否暴力求解。但是动态规划的题用暴力求解通常会超时或者超内存,事实上我在做的时候,复杂度高的动态规划仍然会超时。这道题的测试用例确实很棒,对算法复杂度要求很高。下面先从高复杂的的代码说起,逐步得到“正确”的代码。
解法一(超时):二维动归+遍历
求回文串:逆序判断等(非动态规划)
求切割数: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求最小值,说明需要遍历,此即为二维动归+遍历,代码非常容易写。
复杂度为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):
求回文串:动态规划
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)是回文串。
求切割数:一维动归+备忘录
备忘录就是提前将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]
时间复杂度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]; } };
相关文章推荐
- Android之获取手机上的图片和视频缩略图thumbnails
- android string.xml文件中的整型和string型代替
- Android java 与 javascript互访(相互调用)的方法例子
- android上改变listView的选中颜色
- String.intern
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Prototype源码浅析 String部分(二)
- Ruby中的String对象学习笔记
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- PostgreSQL ERROR: invalid escape string 解决办法
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#冒泡法排序算法实例分析
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- C#实现的算24点游戏算法实例分析
- c语言实现的带通配符匹配算法
- 浅析STL中的常用算法