zoukankan      html  css  js  c++  java
  • [LeetCode] Palindrome Partitioning II

    (Version 1.0)

    这道题是在Palindrome Partitioning的基础之上要求找一个最少分割次数的partitioning,因为之前已经做过了Palindrome Partitioning,所以最开始自然想到了用DP记录所有substring是否是palindrome,然后再对每个substring的mincuts用DP方法逐个由短至长求解,但是在LeetCode OJ上面run大数据时会超时,代码如下:

     1 public class Solution {
     2     public int minCut(String s) {
     3         boolean[][] isPalindrome = new boolean[s.length()][s.length()];
     4         int[][] cuts = new int[s.length()][s.length()];
     5         for (int i = 0; i < isPalindrome.length; i++) {
     6             isPalindrome[i][i] = true;
     7         }
     8         for (int l = 2; l <= s.length(); l++) {
     9             for (int i = 0; i + l <= s.length(); i++) {
    10                 int j = i + l - 1;
    11                 isPalindrome[i][j] = (isPalindrome[i + 1][j - 1] || l == 2) && s.charAt(i) == s.charAt(j);
    12                 if (!isPalindrome[i][j]) {
    13                     cuts[i][j] = Integer.MAX_VALUE;
    14                     for (int k = i; k < j; k++) {
    15                         cuts[i][j] = Math.min(cuts[i][k] + cuts[k + 1][j] + 1, cuts[i][j]);
    16                     }
    17                 }
    18             }
    19         }
    20         return cuts[0][s.length() - 1];
    21     }
    22 }

    看来需要对上面的代码进行优化,去除一些无用功。原题要求的是整个string的mincut,所以求起始位置不是最开头的substring的mincut其实对于问题的解决没有帮助,应该是可以省去的无用功,所以一个可能的优化是将最内层计算cuts数组元素的循环从计算isPalindrome数组的循环最内层拿出去。

    优化最内层计算的核心思想是只考虑从头开始的substring的mincut,其状态的转移也只取决于所有比其短的从头开始的substring的mincut。也就是说,可以把上文提到的DP解法中的两层循环(实际是三层)减小为一层(实际是两层),因为我们现在只需要考虑起始点为输入string的起始的substring,然后每次循环再向末尾添上一个char就可以了。减少了一个变化的量(起始位置),循环层次也就减少了一层,代码如下:

        // a solution from code ganker
        public int minCut(String s) {
            boolean[][] isPalindrome = new boolean[s.length()][s.length()];
            int[] cuts = new int[s.length() + 1];
            for (int i = 0; i < isPalindrome.length; i++) {
                isPalindrome[i][i] = true;
            }
            for (int l = 2; l <= s.length(); l++) {
                for (int i = 0; i + l <= s.length(); i++) {
                    int j = i + l - 1;
                    isPalindrome[i][j] = (isPalindrome[i + 1][j - 1] || l == 2) 
                            && s.charAt(i) == s.charAt(j);
                }
            }
            cuts[0] = -1; // pay attention here, cuts[0] must not be 0!
            for (int i = 0; i < s.length(); i++) {
                cuts[i + 1] = i; // use the fact that the substring of length i + 1 requires at most i cuts
                for (int j = 0; j <= i; j++) {
                    if (isPalindrome[j][i]) { // if s[j:i] is not a palindrome, it doesn't affect the mincut
                        cuts[i + 1] = Math.min(cuts[i + 1], cuts[j] + 1);
                    }
                }
            }
            return cuts[s.length()];
        }

    这里从code ganker的解法(http://blog.csdn.net/linhuanmars/article/details/22837047)中学到了一个技巧,就是要在初始化DP的数组时充分利用现有问题的本身特性。那么这一题的问题背景有什么特性呢?就是一个string所需要的mincut数最大不超过其长度减一,也就是说,极端情况也就是string中最长的palindrome长度为1,这就是为什么代码中初始化DP数组的元素时要给其初始化为所代表的元素index - 1。DP的状态转移描述如下:cuts[i]已经被初始化为最大的可能cut数,所以接下来要做的其实是尝试减小这个值。什么情况下这个值有可能被减小呢?就是在这个substring的substring中有palindrome时。而在这个一维DP中,我们保存的是从输入string头到某一中间位置的substring的mincut,所以内层循环所需要判断,或者说需要处理的当前子问题,就是判断从没被存储的位置开始到当前substring结束这一部分是不是palindrome。

  • 相关阅读:
    最近的题越来越难了,卧槽,搞一上午一题不会,题解也看不懂
    hdu 4630 树状数组 ****
    hdu 3473 划分树 ***
    hdu 3360 最小点覆盖 **
    hdu 1507 记录路径的二分匹配 **
    poj 3177 边双联通 **
    hdu 4612 边双联通 ***
    Elasticsearch聚合后分页深入详解
    redis 五种数据类型及其使用场景
    再有人问你分布式锁,这篇文章扔给他
  • 原文地址:https://www.cnblogs.com/icecreamdeqinw/p/4334176.html
Copyright © 2011-2022 走看看