zoukankan      html  css  js  c++  java
  • 为什么要分治?leetcode0005最长回文子串分治加cache

      上一篇博客贴了该题的暴力递归解法,这次贴一下分治解法。

      分治法是不断的将问题分解,直到分解到最小子问题,然后不断的向上返回,由小问题的解表示大问题的解。

      首先需要了解的是,分治不等于二分,二分法是分治的一个特例。二分法可以直接在每次计算时,将问题规模减半。而普通的分治并没有缩小总的问题规模,只是将问题分解为了多个子问题。

      时间复杂度表示如下:

      二分:通过O(1)的操作,将规模为 n 的问题变成了 n/2 的问题。
      即:T( n ) = T( n / 2 ) + O( 1 )
      分治:通过O(1)的操作,将规模为 n 的问题变成了2个 n/2 的问题。
      即:T( n ) = 2 T( n / 2 ) + O( 1 )

      递推下来:

      二分:

    T( n ) = T( n/2 ) + O( 1 )
    T( n ) = T( n/4 ) + 2 O( 1 )
    T( n ) = T( n/8 ) + 3 O( 1 )
    …
    …[共 logN 次]
    …
    T( n ) = T( 1 ) + logN·O( 1 )
    T( n ) = O( 1 ) + O (logN)
    T( n ) = O(logN)

      分治:

    分治1次
    T( n ) = 2 T( n/2 ) + O( 1 )
    分治2次
    T( n ) = 4 T( n/4 ) + 3 O( 1 )
    分治3次
    T( n ) = 8 T( n/8 ) + 7 O( 1 )
    …
    …[共 logN 次]
    …
    T( n ) = (n) T( 1 ) + (n-1) O ( 1 )
    T( n ) = n*O(1) + O(n-1)
    T( n ) = O(n) + O(n-1)
    T( n ) = O(n)

      可以简单理解为,二分是特殊情况下的分治进行了减枝。

      问题规模不变,听起来似乎没有进行分治的必要。

      但我们比较一下暴力解法于分治解法的递归函数:

      暴力解法: search1(int left, int mid, int depth),以 mid 为中心依靠 left 的移动搜索回文串。 

      分治解法:   dp(int left, int right) ,判断 left - right 之间的字符串是否是回文串。left 与 right 分别设为 i,j,则状态转移方程如下:

      对于暴力解法而言,我们只是单纯的沿着解空间在搜索,函数的入参只需要能够支持我们进行搜索,可以保存临时结果和搜索的坐标就可以了,除此之外并没有特别的语义。

      但对于分治解法,因为我们有分解问题的思想,每一次函数调用,就是在定义和解决一个子问题。入参的集合必须足够我们唯一的定义一个子问题,并且我们定义出了子问题与上级问题间解的关系,这样很容易的我们便可以发现,同层级的问题所需的子问题有许多是重复的。自然的我们可以以入参的集合为坐标建立缓存,避免无效计算。

      其实细想一下,对于暴力搜索,解空间与分治本质上是相同的,也存在着大量的重复计算。

      比如 mid=3,left=0 时我们判断该字符串是否为回文串,我们完全可以根据 mid=3,left=1 的串是否为回文串推导出来。但我们没有定义状态转移方程,很难发现这种重复的结构。我们并不能很清晰的感知到,在计算 mid=3,left=1 时,它的意义是什么,它的结果是否可以复用,我们是否需要缓存这个临时结果。

      子问题的划分,让我们搜索过程中的每一步计算都有了明确的定义,而不只是一味的面向搜索进行计算。这样我们可以更加方便的发现解空间中的重复结构,避免重复计算。 

      相比于暴力搜索,分治的问题规模与解空间的大小完全没有区别。但避免重复计算这一点好处,足够我们优先考虑分治。

      下面是分治的代码,leetcode 中已提交通过:

        public static void main(String[] args) {
            LongestSubString l = new LongestSubString();
            String source = "babadada";
            System.out.println(l.longestPalindromeDP(source));
        }
    
        int maxLength = 0;
        String ans = "";
    
        public final String longestPalindromeDP(String s) {
            if (s == null || s.length() == 0) {
                return "";
            }
            int length = s.length();
            int[][] cache = new int[length][length];
            dp(s, 0, length - 1, cache);
            if (maxLength == 0) {
                return s.substring(0, 1);
            }
            return ans;
        }
    
        private final boolean dp(String source, int left, int right, int[][] cache) {
            if (left >= right) {
                return true;
            }
            if (cache[left][right] == 1) {
                return true;
            }
            if (cache[left][right] == -1) {
                return false;
            }
            char leftChar = source.charAt(left);
            char rightChar = source.charAt(right);
            //头尾不相等,尝试其它可能
            if (leftChar != rightChar) {
                dp(source, left, right - 1, cache);
                dp(source, left + 1, right, cache);
                cache[left][right] = -1;
                return false;
            }
            //头尾相等,判断去掉头尾的子串是否是回文串
            boolean childIsSubstring = dp(source, left + 1, right - 1, cache);
            if (childIsSubstring == false) {
                dp(source, left, right - 1, cache);
                dp(source, left + 1, right, cache);
                cache[left][right] = -1;
                return false;
            }
            //子串是回文串,则直接判断结果。即使尝试其它可能,也不会比主串长度长,所以没有尝试其它可能的必要。
            int length = right - left + 1;
            if (length > maxLength) {
                maxLength = length;
                ans = source.substring(left, right + 1);
            }
            cache[left][right] = 1;
            return true;
        }
  • 相关阅读:
    莫名其妙的float:left; 不能使元素紧贴父级的坑
    将HTML元素转换成图片供用户下载(html2canvas + canvas2Image)
    使用Git代替FTP进行虚拟主机的代码管理
    jQuery: on()特别的几种用法
    监控页面后退前进,浏览器文档加载事件之pageshow、pagehide
    博客园Markdown编辑器试玩~~~
    移动端调试神器(eruda)
    移动端分享插件使用总结
    sqlloader的使用------windows版
    运维工作总结教训
  • 原文地址:https://www.cnblogs.com/niuyourou/p/12466662.html
Copyright © 2011-2022 走看看