zoukankan      html  css  js  c++  java
  • 最长单调递增子序列 [转]

    [转] http://skynewborn.blog.sohu.com/66594610.html

      

    单调子序列包含有单调递增子序列和递减子序列,不失一般性,这里只讨论单调递增子序列。首先,从定义上明确我们的问题。给定序列a1, a2, …, an,如果存在满足下列条件的子序列

    ai1<=ai2<=…<=aim, (其中i1<i2<…<im)

    即称为一个原序列的长度为m的单调递增子序列,那么,现在的问题是我们要找出一个序列的最长的单调递增子序列。

       

    直观上来说,一个序列Sn,它有2n个子序列,枚举所有的子序列,找出其中单调递增的序列,然后返回其中最长的,这样我们的问题就解决了。当然,这个直观的算法在时间上为O(2n*n),它的复杂度增长太快了,所以,我们还应该做得更好一些。

       

    于是,我们换个角度思考。假设我们对Sn排序(递增),得到Sn’。那么,Sn和Sn’的最长公共子序列Cm就是我们要求的最长单调递增子序列(如果你不清楚最长公共子序列的定义,just google it)。为什么?假设Cm’是Sn的最长单调子列,且Cm’!=Cm, Cm’的长度大于Cm。由于Cm’是递增的,并且Cm’的每一个元素都来自Sn,所以Cm’一定是Sn’的子列,而Cm’又是Sn的子列,所以Cm’是Sn和Sn’的公共子列,故Cm’的长度一定小于Cm,这与假设矛盾,所以Cm是最长单调子列。理论上我们的算法是正确的,复杂度方面,运用动态规划(dynamic programming)来求解LCS(最长公共子列,Longest-Common-Subsequence),时间上是O(n2),空间上也是O(n2)。于是,对Sn排序需要nlogn的时间,而LCS需要n2,最后,我们的算法时间上是O(n2)。

       

    可以看到,通过上面的改进,我们的算法效率得到了很大的提升(从指数增长到多项式增长)。不过,程序设计的乐趣就是它会不断地给我们一些惊喜,所以,就此打住不是我们该做的,于是,更好的算法应该是存在的。

       

    对于序列Sn,考虑其长度为i的单调子列(1<=i<=m),这样的子列可能有多个。我们选取这些子列的结尾元素(子列的最后一个元素)的最小值。用Li表示。易知

    L1<=L2<=…<=Lm

    如果Li>Lj(i<j),那么去掉以Lj结尾的递增子序列的最后j-i个元素,得到一个长度为i的子序列,该序列的结尾元素ak<=Lj<Li,这与Li标识了长度为i的递增子序列的最小结尾元素相矛盾,于是证明了上述结论。现在,我们来寻找Sn对应的L序列,如果我们找到的最大的Li是Lm,那么m就是最大单调子列的长度。下面的方法可以用来维护L。

       

    从左至右扫描Sn,对于每一个ai,它可能

    (1)    ai<L1,那么L1=ai

    (2)    ai>=Lm,那么Lm+1=ai,m=m+1 (其中m是当前见到的最大的L下标)

    (3)    Ls<=ai<Ls+1,那么Ls+1=ai

       

    扫描完成后,我们也就得到了最长递增子序列的长度。从上述方法可知,对于每一个元素,我们需要对L进行查找操作,由于L有序,所以这个操作为logn,于是总的复杂度为O(nlogn)。优于开始O(n2)的算法。这里给出我的一个实现:(算法并没有返回具体的序列,只是返回长度)

      

     1 template <typename T>
    2 int LMS (const T * data, int size)
    3 ...{
    4 if (size <= 0)
    5 return 0;
    6
    7 T * S = new T[size];
    8 int S_Count = 1;
    9 S[0] = data[0];
    10
    11 for (int i = 1; i < size; i++)
    12 ...{
    13 const T & e = data[i];
    14 int low = 0, high = S_Count - 1;
    15
    16 while (low <= high)
    17 ...{
    18 int mid = (low + high) / 2;
    19
    20 if (S[mid] == e)
    21 break;
    22 else if (S[mid] > e)
    23 ...{
    24 high = mid - 1;
    25 }
    26 else
    27 ...{
    28 low = mid + 1;
    29 }
    30 }
    31
    32 //well, in this point
    33 //high is -1, indicating e is the smallest element.
    34 //otherwise, high indicates index of the largest element that is smaller than e
    35 if (high == S_Count - 1)
    36 S[S_Count++] = e;
    37 else
    38 S[high + 1] = e;
    39 }
    40
    41 return S_Count;
    42 }

      

    另:最长单调子序列及其计数问题

    http://hi.baidu.com/vincentz/blog/item/6df5a3c2be3ffa180ef477e2.html

       

  • 相关阅读:
    SBIT
    Linux 系统中进程5中常见状态
    centos yum command
    About DNS
    【从零开始学BPM,Day1】工作流管理平台架构学习
    打破陈规抓痛点,H3 BPM10.0挑战不可能
    H3 BPM让天下没有难用的流程之产品概述
    《中国BPM品牌竞争力指数》完整版
    H3 BPM的品牌制胜之道
    《中国BPM品牌竞争力指数》报告出炉,H3 BPM持续领跑
  • 原文地址:https://www.cnblogs.com/longdouhzt/p/2382296.html
Copyright © 2011-2022 走看看