132. Palindrome Partitioning II


July-14-2019

日他妈好难啊
第一种做法,就是类似于palindrome-partition那样,枚举出所有可能,选一个substring数目最少的。

第二种做法DP,看到DP真几把绝望。
minCut[n]代表0~n的substring最少能切多少下,使所有substring都是palindrome
minCut[n]有2种可能
1 - 本身是palindrome, 那么minCut[n] = 0
2 - 本身不是,那它等于是0~(n-1)的substring又添加了最后一个char. 最后添加的字母肯定是palindrome,所以 minCut[n] = minCut[n-1] + 1
有人说,遇到这种情况怎么办,abacac (minCut[5])
abaca, minCut[4]=2: aba|a|c
minCut[5] = minCut[4] + 1 = 3,结果是错的,因为可以 aba|cac
于是,在刚才的2的情况下,minCut[n-1] + 1是minCut[n]的最差情况。。
实际上要遍历各种情况,分为左边和右边:
minCut[0] + 1~n
minCut[1] + 2~n
minCut[2] + 3~n
...
首先要判断右边是不是palindrome,不是的话不用看了。上面的abacac minCut[5]的最优解就在
minCut[2] + 3~5
判断palindrome 1~n也可以DP MEM来记录。
1~5, 2~5, 3~5判断是不是palindrome的时候,其实是先看两端是不是一样,
拿1~5来说,先看两端1和5是不是一样,一样的话再看中间2-4是不是一样,2-4在计算minCut[4]的时候已经算出来过,就不用重新算了。

讲是讲明白了,想我肯定是想不出来的。 看一刷的时候我说这个题不难,我为什么要装逼?为什么?

class Solution {
    public int minCut(String s) {
        if (s.length() <= 1) return 0;
        int[] minCut = new int[s.length()];
        boolean[][] isPalindrome = new boolean[s.length()][s.length()];
        
        for (int i = 0; i < s.length(); i ++) {
            int maxCutFrom0ToI = i;
            int tempCut = maxCutFrom0ToI;
            
            for (int j = 0; j <= i; j ++) {
                //                                  (j+1) >= (i-1)  only 1 or 0 letter between j and i
                if (s.charAt(i) == s.charAt(j) && ((j + 1) >= (i - 1) || isPalindrome[j + 1][i - 1])) {
                    isPalindrome[j][i] = true;
                    if (j == 0) {
                        // entire i~j is palindrome which requires 0 cut
                        tempCut = 0;
                    } else {
                        tempCut = Math.min(tempCut, minCut[j - 1] + 1);
                    }
                    
                }
            }
            
            minCut[i] = tempCut;
        }
        return minCut[s.length() - 1];
    }
}

二刷

dp[i]表示从0 - i的最小切数。

dp[i]怎么算呢。。(他和前面的关系是什么?)

假如0 ~ i是回文,那最小切就是0.

能想到这写代码还是很不好写,我回头看有点弄不懂是怎么想出来的,实际解法比代码看起来要复杂的多。。感觉是用了2次DP。。

拿一段0 ~ n来说,他本身不是回文。 (是的话不用算了)

看看能不能分成0 ~ (i-1) | i ~ n 两段
左边用dp的mem来记忆能还是不能,右边楞判断,成功的话,就是dp[n] = dp[i-1] + 1

说是这么说,但是思路怎么来的很奇怪,我第一想法是

dp[0][i-1] + 1 + dp[i][n]这种二位的DP。。

简单地说这应该是个什么套路,但是我没总结出来。。

public class Solution {
    public int minCut(String s) {
        if (s.length() <= 1) return 0;
        
        // from 0 to n
        int[] dp = new int[s.length()]; 
        boolean[][] mem = new boolean[s.length()][s.length()];
        
        for (int i = 0; i < s.length(); i++) {
            int from0toI = i;
            for (int j = 0; j <= i; j++) {
                if (s.charAt(i) == s.charAt(j) && (j + 1 >= i - 1 || mem[j+1][i-1])) {
                    mem[j][i] = true;
                    if (j == 0) {
                        from0toI = 0;
                    } else {                            
                        from0toI = Math.min(from0toI, dp[j - 1] + 1);
                    }
                }
            }
            dp[i] = from0toI;
        }
        return dp[s.length() - 1];
    }
}
原文地址:https://www.cnblogs.com/reboot329/p/6065214.html