题目
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.
思路一
用递归得到所有可能的划分,可以参考上篇博文Palindrome Partitioning,然后求取最短的划分。
但是我们没必要存取所有的划分结果,只需要存储划分段数就可以了。
对空间的改进
class Solution {
public:
int minCut(string s) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int len = s.length();
if(len<1) return 0;
vector<string> vec;
int min = INT_MAX;
mypart(vec, 0, s, min);
return min-1;
}
void mypart(vector<string> &vec, int cur, string &S, int &min) {
if(cur==S.length()) {
if(vec.size()<min)
min = vec.size();
return ;
}
for(int i=cur;i<S.length();i++) {
int len = i-cur+1;
string str = S.substr(cur,len);
if(ispalind(str)) {
vec.push_back(str);
mypart(vec, i+1, S, min);
vec.pop_back();
}
}
}
bool ispalind(string &S) {
if(S.empty())
return true;
int i=0, j=S.length()-1;
while(i<j) {
if(S[i]!=S[j])
return false;
i++;
j--;
}
return true;
}
};
对大数据虽然没有内存限制了,但是还是出现了 Run Status: Time Limit Exceeded
因为我们还可以进一步的 剪枝优化 步的:如果当前子字符串生成的回文划分段数已经超过了 min ,就没必要接着进行下去了。
剪枝优化时间
for(int i=cur;i<S.length();i++) {
int len = i-cur+1;
string str = S.substr(cur,len);
if(vec.size()<=min && ispalind(str)) {
vec.push_back(str);
mypart(vec, i+1, S, min);
vec.pop_back();
}
}
或者
if(vec.size()+1>min)
return ;
for(int i=cur;i<S.length();i++) {
int len = i-cur+1;
string str = S.substr(cur,len);
if(ispalind(str)) {
vec.push_back(str);
mypart(vec, i+1, S, min);
vec.pop_back();
}
}
但好像都还是未能通过大数据。
原因是 虽然加入剪枝,但是算法的基本思想还是遍历,数量级未改变,依旧是 O(N*N) 。
下面考虑要降低数量级了。
思路二
用DP动态规划的思想:
二维标志数组 ispalin[i][j] 记录 s 的 i~j 子串是否是回文的;
一维数组 num[i] 记录 s 的 i~len-1 分割后的最小段数,初始化时 num[0]=len, ... , num[len-1]=1;
最优结果是 num[0]-1 。
动态规划过程:
(1)如果s[i]=s[j] 并且 ispalin[i+1][j-1]=true ,则表示 i~j 也是回文,即 ispalin[i][j]=true。
(2)如果s[i]=s[j] 并且 ispalin[i][j]=true,则 num[i] = min(num[i], num[j+1]+1) ,即更新当前 i~len-1子串的最小段数。
class Solution {
public:
int minCut(string s) {
// Start typing your C/C++ solution below
// DO NOT write int main() function
int len = s.length();
if(len<2) return 0;
vector<vector<bool>> ispalin(len,vector<bool>(len,false));
vector<int> num(len+1,0);
for(int i=0;i<len;i++)
num[i] = len-i;
for(int i=len-1;i>=0;i--) {
for(int j=i;j<len;j++) {
if(s[i]==s[j]) {
if(j-i<2) {
ispalin[i][j]=true;
num[i] = min(num[i],num[j+1]+1);
}else if(ispalin[i+1][j-1]) {
ispalin[i][j]=true;
num[i] = min(num[i],num[j+1]+1);
}
}
}
}
return num[0]-1;
}
};
其实时间复杂度也是 O(N*N)的,但是对大数据轻松通过了。
与思路一比较:
优点在,对当前的子串是否是回文的判断上要更优,避免重复判断。
例如:如果 s1=abba 是回文,当增加长度时 s2=dabbad ,
思路一:再遍历一遍 s2 ,时间 O(length(s2))
思路二:比较新增加的字符是否相等,同时判断s1是否是回文,时间O(2+1)
所以高下优劣立见。
如果仔细分析计算:
(1)思路一的时间复杂度是 O(N*N*length(str))
(2)DP的时间复杂度是 O(N*N*2)