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.
Example:
Input: “aab”
Output: 1
Explanation: The palindrome partitioning [“aa”,“b”] could be produced using 1 cut.
方法一(普通DP):
dp[ i ]表示0~i 的子串变成回文子串需要的min cut
先把dp中初始化到最大切割数,即字符串长度n
valid[ i ] [ j ] 表示判断i ~ j 子串是否为回文子串
把判断0~i所需要的min cut问题分割成子问题,即假设j+1~n的子串是回文子串,那么0~i的min cut即为0~j的min cut + 1(分割j 和 j+1本身需要一次cut)与 0 ~ i 的min cut中的较小者
判断i~j子串是否是回文子串的方法,如果s[i] == s[j],那么需要再判断I+1 ~ j+1子串是否有效, 即valid[i+1][j+1]
public int minCut(String s) {
if(s == null || s.length() == 0) {
return 0;
}
int n = s.length();
int[] dp = new int[n];
Arrays.fill(dp, n);
boolean[][] valid = new boolean[n][n];
for(int i = 0; i < n; i++) {
Arrays.fill(valid[i], true);
}
for(int l = 2; l <= n; l++) {
for(int i = 0, j = i + l - 1; j < n; i++, j++) {
valid[i][j] = s.charAt(i) == s.charAt(j) && valid[i+1][j-1];
}
}
for(int i = 0; i < n; i++) {
if(valid[0][i]) {
dp[i] = 0; //0~i如果为回文子串,则不需要分割
continue;
}
for(int j = 0; j < i; j++) {
if(valid[j+1][i]) { //后半段有效,需要的分割数为前半段的min cut + 1和整体分割数的较小者
dp[i] = Math.min(dp[i], dp[j] + 1);
}
}
}
return dp[n-1];
}
方法二(中心点扩散):
class Solution {
public:
int minCut(string s) {
vector<int> dp(s.size());
for(int i=0;i<s.size();i++){
dp[i] = i;
}
for(int i =0; i<s.size();i++){
int len = 0;
//奇数情况:以i为中心点向两边扩散,也是分成两段,前半段是扩散出的回文子串前面的部分,后半段是扩散出的回文子串,和方法一思想类似
for(len=0;i-len>=0&&i+len<s.size()&&s[i-len]==s[i+len];){
len++;
if(i+1-len==0){
dp[i+len-1] = 0;
}
else{
dp[i+len-1] = min(dp[i+len-1],dp[i-len]+1);
}
}
//偶数情况:以i和i+1为中心点向两边扩散,也是分成两段,前半段是扩散出的回文子串前面的部分,后半段是扩散出的回文子串,和方法一思想类似
for(len = 0; i-len>=0&&i+1+len<s.size()&&s[i-len]==s[i+1+len];){
len++;
if(len>0){
if(i+1-len==0){
dp[i+len] = 0;
}
else{
dp[i+len] = min(dp[i+len],dp[i-len]+1);
}
}
}
}
return dp[s.size()-1];
}
};