LeetCode 原题链接 :
https://oj.leetcode.com/problems/palindrome-partitioning-ii/
对于给定字符串,求最少需要几次划分,能够将字符串划分为若干子串,每个子串都是一个回文串。如abaab,需要至少2次划分,将字符串划分为:aba|a|b,每个部分均为回文串。
经典的DP题目。
我们可以分解为DP问题:
参考文章: http://fisherlei.blogspot.com/2013/03/leetcode-palindrome-partitioning-ii.html
[Thoughts]
凡是求最优解的,一般都是走DP的路线。这一题也不例外。首先求dp函数,
定义函数
D[i,n] = 区间[i,n]之间最小的cut数,n为字符串长度
a b a b b b a b b a b a
i n
如果现在求[i,n]之间的最优解?应该是多少?简单看一看,至少有下面一个解
a b a b b b a b b a b a
i j j+1 n
此时 D[i,n] = min(D[i, j] + D[j+1,n]) i<=j <n。这是个二维的函数,实际写代码时维护比较麻烦。所以要转换成一维DP。如果每次,从i往右扫描,每找到一个回文就算一次DP的话,就可以转换为
D[i] = 区间[i,n]之间最小的cut数,n为字符串长度, 则,
D[i] = min(1+D[j+1] ) i<=j <n
有个转移函数之后,一个问题出现了,就是如何判断[i,j]是否是回文?每次都从i到j比较一遍?太浪费了,这里也是一个DP问题。
定义函数
P[i][j] = true if [i,j]为回文
那么
P[i][j] = str[i] == str[j] && P[i+1][j-1];
public class Solution {
public int minCut(String s) {
if (s == null || s.length() == 0) {
return 0;
}
int len = s.length();
int[] D = new int[len];
boolean[][] isPalid = new boolean[len][len];
for (int i = len - 1; i >= 0; i--) {
// the worst case is divide the word one by one.
D[i] = len - 1 -i;
for (int j = i; j <= len - 1; j++) {
// init it to be false;
isPalid[i][j] = false;
if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || isPalid[i + 1][j - 1])) {
isPalid[i][j] = true;
if (j == len - 1) {
D[i] = 0;
} else {
// 如果前半部分是回文,那么我们可以分解为第一个回文 + 后半部分的最小分割数
D[i] = Math.min(D[i], D[j + 1] + 1);
}
}
}
}
return D[0];
}
}