最长上升子序列(LIS)O(nlogn)优化

LIS问题:
求数组A[i]的最长(严格)上升子序列的元素个数。

先看一看O(n2)的动态规划算法,定义d[i]为以A[i]作为结尾的LIS长度,则d[i]=max{d[j]+[A[i]>A[j]]}(j<i),边界是d[0]=1,答案是d[n-1]。由于每次都要枚举比i小的所有j,所以时间复杂度是O(n2)的。

每个d[i]的计算,会用到所有的d[j](j<i),我们注意到如果这之中有d[j1]==d[j2]==...==d[jk]的一系列j,我们只需要用到它们对应的A[j1]、A[j2]、...、A[jk]值最小的那一个。所以对于d[j]相同的一系列下标,我们只保留1个。

所以我们建立d的值到下标的映射f:x→j,即f[x] = (满足d[j]==x的j中A[j]最小的j),对于当前还不可行的x,令f[x]=∞。对于任意可行的x0,1、2、...、x0-1一定也都是可行的。

假如有了映射f,计算d[i]时就依次查看f[i]、f[i-1]、f[i-2]、...、f[1]的值,找到第一个满足A[f[k]]<A[i]的k,就有d[i]=k+1。但是f[x]不一定是单调的,所以没办法快速查找,于是我们建立映射g,g[x]=A[f[x]],g[x]的定义比较绕:满足d[j]==x的j中A[j]最小的j的A[j]值,可以证明它是单调的。

假如有了映射g,计算d[i]时就查找最大的k使得g[k]<A[i],就有d[i]=k+1,由于g是单调的,所以可以二分查找。

按照上面那样算完d[i]后,我们需要维护g,g[1]、g[2]、...、g[k]不需要更新,因为A[i]比这些值要大,A[i]的出现并没有使得它们可以以更小的元素作为结尾,g[k+2]以及之后的g不需要更新,因为A[i]虽然小于等于这些值,但以A[i]结尾的LIS也就是k+1的长度,g[k+2]中存的是LIS长度为k+2的序列中结尾的最小值。只需要更新g[k+1]=A[i],因为以A[i]结尾的LIS长度为k+1,且g[k+1]>=A[i]。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 在C语言中,五种基本数据类型存储空间长度的排列顺序是: A)char B)char=int<=float C)ch...
    夏天再来阅读 3,428评论 0 2
  • var navigator = navigator || {};var window = window || {}...
    DF_Sky阅读 1,310评论 0 0
  • "use strict";function _classCallCheck(e,t){if(!(e instanc...
    久些阅读 2,062评论 0 2
  • 抛开世间所有,还剩下什么呢?没有未来的选择,心已死,梦何处生,不知所得,终知所失,一切结束的过往曾经,烟消云散...
    zs123阅读 249评论 2 2
  • 是的 我所谓的好在你们眼里都那么肤浅,你一再哗然 你所谓的好在我眼里也不尽而已 生活没有按照我们所想的那样生活...
    訂大大阅读 166评论 0 0