132-分隔字符串,得到回文II

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/LaputaFallen/article/details/79981374

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 ;
        }
    }
}
阅读更多

没有更多推荐了,返回首页