问题描述
- 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.
问题分析
- 该题类似于 LeetCode 131. Palindrome Partitioning ,只不过131是枚举出所有的可能性,要用 DFS + 回溯。 而该题是求一个统计数目的最值问题,要用 动态规划。
- 首先尝试用 递归去做,因为存在大量重复计算,不出意外,TLE。
- 然后用动态规划去做,该题其实是涉及到两个动态规划的
minPart[i]
表示chs[i ~ length] 能形成的最小部分数
状态转移方程为minPart[i] = 1 + min(minPart(j + 1)) , i~j 是回文串;
而我们在求minPart[i]
的过程中,需要判断回文,回文的判断同样也用动态规划isPalindrome[i][j]
表示chs[i..j] 是否是回文串
状态转移条件为
if (chs[i] == chs[j] && (i + 1 >= j - 1 || isPalindrome[i + 1][j - 1])) { //i ~ j 是回文串
isPalindrome[i][j] = true;
}
- 初始状态:
minPart[len] = 0
, 然后从后往前填minPart
数组,在填该数组的过程中,同时把isPalindrome[i][j]
由下及上,由左及右填好该二维表的右上部分。
经验教训
- 动态规划和 DFS 一样,DFS要明确知道递归函数的意义,找到递归关系以及 base case。
而动态规划则是要明确dp[i], dp[i][j]
的意义,找好状态转移关系(依赖条件),以及初始状态。 - 和131 题对比,知道 DFS + 回溯 与 动态规划 的不同应用场景。
枚举出所有的可能性,要用 DFS + 回溯。 求一个最值问题,最优问题,统计数目,要用 动态规划。
代码实现
class Solution {
public int minCut(String s) {
if (s == null) {
return 0;
}
char[] chs = s.toCharArray();
//isPalindrome[i][j] 表示chs[i..j] 是否是回文串
boolean[][] isPalindrome = new boolean[chs.length][chs.length];
//minPart[i] 表示chs[i ~ length] 能形成的最小部分数
int[] minPart = new int[chs.length + 1];
//初始化
minPart[chs.length] = 0;
for (int i = chs.length - 1; i >= 0; i--) { //从后到前填minPart
//minPart[i] = 1 + min(minPart(j + 1)) , i~j 是回文串;
int min = chs.length;
for (int j = i; j < chs.length; j++) {
if (chs[i] == chs[j] && (i + 1 >= j - 1 || isPalindrome[i + 1][j - 1])) { //i ~ j 是回文串
isPalindrome[i][j] = true;
min = Math.min(min, minPart[j + 1]);
}
}
minPart[i] = min + 1;
}
//因为是求最小切割数,那么是最小部分数 - 1
return minPart[0] - 1;
}
/*
//dfs做法,会超时
public int minCut(String s) {
if (s == null) {
return 0;
}
char[] chs = s.toCharArray();
return minCut(chs, 0) - 1;
}
public int minCut(char[] chs, int i) {
if (i == chs.length) {
return 0;
}
int minCut = chs.length;
for (int k = i; k < chs.length; k++) {
if(isValid(chs, i, k)) {
minCut = Math.min(minCut, minCut(chs, k + 1));
}
}
return 1 + minCut;
}
public boolean isValid(char[] chs, int begin, int end) {
while (begin < end) {
if(chs[begin] != chs[end]) {
return false;
}
++begin;
--end;
}
return true;
}
*/
}