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.
Analysis:
This is a DP problem! Also it is a double DP problem!
Why? Because, if we use the same algorithm in the Palindrome Partitioning I, definitely it will expire the time limit. When you are facing the problem asking "return the minimum/maximum, best, shortest...", it is also a good direction targeting the DP (sometimes greedy also works fine). If you are not familiar with DP problem, here is a very good reading from topcoder. Generally speaking, DP is not very easy, and most of the company interviews will NOT cover the DP, so don't worry too much.
For this problem, firstly we consider the main part.
It is a good way to look for the "state", and the "optimal solution" based on that state, to solve the DP problem. In other words, if you found the correct state, and the function of the state representing the optimal solution, all you need is some loops and initialization implementing the algorithm.
Here the state is not too hard ---- minimum cut. Define res[i] = the minimum cut from 0 to i in the string.
The result w =e need eventually is res[s.size()-1].
We know res[0]=0. Next we are looking for the optimal solution function f.
For example, let s = "leet".
f(0) = 0; // minimum cut of str[0:0]="l", which is a palindrome, so not cut is needed.
f(1) = 1; // str[0:1]="le" How to get 1?
f(1) might be: (1) f(0)+1=1, the minimum cut before plus the current char.
(2) 0, if str[0:1] is a palindrome (here "le" is not )
f(2) = 1; // str[0:2] = "lee" How to get 2?
f(2) might be: (1) f(1) + 1=2
(2) 0, if str[0:2] is a palindrome (here "lee" is not)
(3) f(0) + 1, if str[1:2] is a palindrome, yes!
f(3) = 2; // str[0:3] = "leet" How to get 2?
f(3) might be: (1) f(2) + 1=2
(2) 0, if str[0:3] is a palindrome (here "leet" is not)
(3) f(0) + 1, if str[1:3] is a palindrome (here "eet" is not)
(4) f(1) + 1, if str[2:3] is a palindrome (here "et" is not)
OK, output f(3) =2 as the result.
So, the optimal function is:
f(i) = min [ f(j)+1, j=0..i-1 && str[j:i] is palindrome
0, if str[0,i] is palindrome ]
The above algorithm works well for the smaller test cases, however for the big cases, it still cannot pass.
Why? The way we test the palindrome is time-consuming.
Also using the similar DP idea, we can construct the look-up table before the main part above, so that the palindrome testing becomes the looking up operation. The way we construct the table is also the idea of DP.
e.g. mp[i][j]==true if str[i:j] is palindrome.
mp[i][i]=true;
mp[i][j] = true if str[i]==str[j] and (mp[i+1][j-1]==true or j-i<2 ) j-i<2 ensures the array boundary.
So, using this scheme to test the palindrome helps improve the efficiency and thus we can pass all the test cases. Details see the code below.
java
public int minCut(String s) {
int len = s.length();
int []res = new int[len];
boolean[][] pp = new boolean[len][len];
for(int i=len-1;i>=0;i--){
for(int j=i;j<len;j++){
if(s.charAt(j)==s.charAt(i)&& (j-i<2 || pp[i+1][j-1])){
pp[i][j] = true;
}else {
pp[i][j] = false;
}
}
}
for(int i=0;i<len;i++){
int ms = len;
if(pp[0][i]){
res[i] = 0;
}else {
for(int j=0;j<i;j++){
if(pp[j+1][i] && ms>res[j]+1)
ms = res[j]+1;
}
res[i] = ms;
}
}
return res[len-1];
}
c++
int minCut(string s) {
int len = s.size();
int* dp = new int[len+1];
for(int i=len; i>=0; i--)
dp[i] = len-i;
bool** matrix = new bool*[len];
for(int i=0; i<len; i++)
{
matrix[i] = new bool[len];
memset(matrix[i], false, sizeof(bool)*len);
}
for(int i=len-1; i>=0; i--)
for(int j=i; j<len; j++)
{
if(s[i] == s[j] && (j-i<2 || matrix[i+1][j-1]))
{
matrix[i][j] = true;
dp[i] = min(dp[i], dp[j+1]+1);
}
}
return dp[0]-1;
}