Description
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.
问题描述
给定字符串s,对其分段,使得每个子串为回文串。
返回最小切分数
问题分析
两种解法,DFS + Memorization和动态规划
动态规划
状态为dp[i], 表示[i, len - 1]需要的最小切分数
转移
若s.substring(i, j + 1)为回文,那么dp[i] = Math.min(dp[i + 1] + 1, dp[i])
需要注意,每轮更新dp[i]时先将其设置为len - i - 1(最大切分数)
解法1(DFS + Memorization)
class Solution {
public int minCut(String s) {
int len = s.length();
Map<String, Integer> memo = new HashMap();
//注意这里需要先放入空串,其对应切分数为0
memo.put("", 0);
return dfs(s, memo) - 1;
}
public int dfs(String s, Map<String, Integer> memo){
if(memo.containsKey(s)) return memo.get(s);
int min = s.length();
for(int i = s.length() - 1;i >= 0;i--){
if(isPalindrome(s, 0, i)){
min = Math.min(min, 1 + dfs(s.substring(i + 1), memo));
}
//注意这里,若得到1或者2,退出循环,因为1或者2已经是最小的结果了
if(min == 1 || min == 2) break;
}
memo.put(s, min);
return min;
}
//判断是否为回文
public boolean isPalindrome(String s, int left, int right){
while(left < right){
if(s.charAt(left) != s.charAt(right)) return false;
left++;
right--;
}
return true;
}
}
解法2(动态规划)
class Solution {
public int minCut(String s) {
int len = s.length();
int[] dp = new int[len];
//回文bool数组,若pal[i][j] = true,表示s.substring(i, j + 1)为回文
boolean[][] pal = new boolean[len][len];
for(int i = len - 1;i >= 0;i--){
dp[i] = len - i - 1;
for(int j = i;j < len;j++){
//判断s.substring(i, j + 1)是否为回文
if(s.charAt(i) == s.charAt(j) && (j - i < 2 || pal[i + 1][j - 1])){
//若是回文,将pal[i][j]标为true
pal[i][j] = true;
//若j == len - 1,说明s.substring(i, len)为回文,直接为0
if(j == len - 1) dp[i] = 0;
else dp[i] = Math.min(dp[i], dp[j + 1] + 1);
}
}
}
return dp[0];
}
}
解法3(最优解)
/*
.......aba...
|<-X->| ^
|<---Y-->|
看下这个例子,aba 为回文串,那么Y的最小切分数一定不大于X + 1
这就是这个算法的核心思想
通过由i向两端遍历得到奇数长度回文串和偶数长度回文串,s[e] = Math.min(s[e], s[s - 1] + 1)
*/
class Solution {
public int minCut(String s) {
if (s == null || s.length() < 2) return 0 ;
int N = s.length() ;
int C[] = new int[N] ;
Arrays.fill(C, N-1) ;
char[] str = s.toCharArray() ;
for (int l = 0 ; l < N ; ++ l) {
//奇数长度的回文串
updateCutForPalindrome(str, l, l, C, N) ;
//偶数长度的回文串
updateCutForPalindrome(str, l, l+1, C, N) ;
}
return C[N-1] ;
}
void updateCutForPalindrome(char[] str, int s, int e, int[] C, int N){
while ( s >= 0 && e < N && str[s] == str[e]) {
C[e] = Math.min(C[e], (s-1 >= 0 ? C[s-1] : -1) + 1) ;
++ e ;
-- s ;
}
}
}