# 132 - Palindrome Partitioning II
解法一 - 暴力法
暴力法就是要試試看每一種可能的 substring 組合,看哪一種組合可以滿足每一個 substring 都是 palindrome,而且分割數最少。
class Solution {
public:
int minCut(string s) {
return findMinCut(s, 0, s.length()-1);
}
private:
int findMinCut(string& s, int start, int end) {
// if s is empty or s is palindrome, minCut == 0
if(start >= end || isPalindrome(s, start, end))
return 0;
// the maximal number of cut == the number of all length-1 pieces
int minCut = end-start;
// finetune 到這樣會過,但我不太懂為何不是
// for(int I=start+1; i<end; i++)
for(int i=start; i<=end; i++) {
if(isPalindrome(s, start, i)) {
minCut = min(minCut, 1+findMinCut(s, i+1, end));
}
}
return minCut;
}
bool isPalindrome(string& s, int i, int j) {
while(i < j) {
if(s[i++] != s[j--])
return false;
}
return true;
}
};
暴力解仍然不意外地會 TLE,讓我們繼續優化下去。
解法二 - 暴力法 + memoization
對於 findMinCut() 跟 isPalindrome() 這兩個 function,我們都可以做 memoization,雖然這兩者會改變的都只有 start,不過方便起見,就都先用 2D 的 array 來記錄。
class Solution {
public:
int minCut(string s) {
vector<vector<int>> memo(s.length(), vector<int>(s.length(), -1));
vector<vector<int>> memoIsPalindrome(s.length(), vector<int>(s.length(), -1));
return findMinCut(memo, memoIsPalindrome, s, 0, s.length()-1);
}
private:
int findMinCut(vector<vector<int>>& memo, vector<vector<int>>& memoIsPalindrome, string& s, int start, int end) {
// if s is empty or s is palindrome, minCut == 0
if(start >= end || isPalindrome(memoIsPalindrome, s, start, end))
return 0;
// the maximal number of cut == the number of all length-1 pieces
if(memo[start][end] == -1) {
int minCut = end-start;
for(int i=start; i<=end; i++) {
if(isPalindrome(memoIsPalindrome, s, start, i)) {
minCut = min(minCut, 1+findMinCut(memo, memoIsPalindrome, s, i+1, end));
}
}
memo[start][end] = minCut;
}
return memo[start][end];
}
bool isPalindrome(vector<vector<int>>& memoIsPalindrome, string& s, int i, int j) {
int x=i, y=j;
if(memoIsPalindrome[i][j] == -1) {
memoIsPalindrome[i][j] = 1;
while(x < y) {
if(s[x++] != s[y--]) {
memoIsPalindrome[i][j] = 0;
break;
}
}
}
return memoIsPalindrome[i][j] == 1;
}
};
總算是過了!雖然效率有點差:
Runtime: 316 ms, faster than 19.07% of C++ online submissions for Palindrome Partitioning II. Memory Usage: 35.3 MB, less than 40.00% of C++ online submissions for Palindrome Partitioning II.
解法三 - DP
經過解法二的淬煉,我們現在知道,需要兩個 dp table,一個紀錄 isPalindrome,一個紀錄 minCuts,實作出來如下:
class Solution {
public:
int minCut(string s) {
// isPalindrome[i][j] == true if s[i] - s[j] is a palindrome
vector<vector<bool>> isPalindrome(s.length(), vector<bool>(s.length(), false));
// every string with one character is a palindrome
for (int i = 0; i < s.length(); i++) {
isPalindrome[i][i] = true;
}
// build isPalindrome table
for (int start = s.length() - 1; start >= 0; start--) {
for (int end = start + 1; end < s.length(); end++) {
if (s[start] == s[end]) {
// if it's a two character string or if the remaining string is a palindrome too
if (end-start == 1 || isPalindrome[start+1][end-1]) {
isPalindrome[start][end] = true;
}
}
}
}
// now lets populate the second table, every index in 'cuts' stores the minimum cuts needed
// for the substring from that index till the end
vector<int> cuts(s.length(), 0);
for (int start = s.length() - 1; start >= 0; start--) {
int minCuts = s.length()-start; // maximum cuts
for (int end = s.length() - 1; end >= start; end--) {
if (isPalindrome[start][end]) {
// we can cut here as we got a palindrome
// also we dont need any cut if the whole substring is a palindrome
minCuts = (end == s.length() - 1) ? 0 : min(minCuts, 1 + cuts[end+1]);
}
}
cuts[start] = minCuts;
}
return cuts[0];
}
};
Last updated