• 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];
        }
    }
    
  • 相关阅读:
    K
    A
    2017 Multi-University Training Contest
    第一章 概述
    校赛F 比比谁更快(线段树)
    POJ 3683 Priest John's Busiest Day
    POJ 2186 Popular Cows
    第十五周讨论
    线段树模板(单点更新,区间更新,RMQ)
    2-SAT问题(白书)
  • 原文地址:https://www.cnblogs.com/reboot329/p/6065214.html
Copyright © 2020-2023  润新知