关于最长递增子序列问题的求解(LIS)

本文会介绍几种复杂度不同的算法,并给出实现。

 

一、转化为最长公共子序列(LCS)求解

二、普通dp

三、LIS的O(nlogn)算法


 

问题描述:

设L=<a1,a2,…,an>是n个不同的实数的序列,L的递增子序列是这样一个子序列Lin=<aK1,ak2,…,akm>,其中k1<k2<…<km且aK1<ak2<…<akm。求最大的m值。

很明显注意到子序列跟子串是有区别的。

 

一、转化为最长公共子序列(LCS)求解 , O(n2)

设序列X=<b1,b2,…,bn>是对序列L=<a1,a2,…,an>按递增排好序的序列。那么显然X与L的最长公共子序列即为L的最长递增子序列。这样就把求最长递增子序列的问题转化为求最长公共子序列问题LCS了。

最长公共子序列问题用动态规划的算法可解。可以参考这道题的分析

 

二、普通dp, O(n2)

(一)、记忆化搜索解dp

①从后向前规划

d(i)表示L中以ai为末元素的最长递增子序列的长度

d[i] = max{dj+1 | ai>aj, j<i};

②从前向后规划
从前向后找,但是di为前元素,注意(*)标记处的不同

d[i] = max{dj+1 | ai<aj, j = i+1~n-1};

在某些情况下会要使用这种规划 ,如做过的一道最长增减子序列的题,由于开始的时候必须先增。


(二)、递推法解dp
我们依次遍历整个序列,每一次求出从第一个数到当前这个数的最长上升子序列,
直至遍历到最后一个数字为止,然后再取d数组里最大的那个即为整个序列的最长上升子序列。
我们用d[i]来存放序列0~i-1的最长上升子序列的长度,那么d[i]=max{d[j])+1 | a[i]>a[j] , j∈[0, i-1]}.
显然d[0]=1,我们从i=1开始遍历后面的元素即可。

 

三、LIS的O(nlogn)算法

 

step 1
在以上算法中,计算d[i]时,都要找出最大的d[j](j<i,aj<ai)来。
由于d数组无序,只能顺序查找满足的d[j],如果能让d组有序
就可以使用二分查找,这样算法复杂度就可以降低到O(nlogn)。
于是构造F(d[i]) = a[i]. 表示长度为d[i]的子序列最小末尾元素为a[i]。
则问题的解为最后更新到的F下标(最大长度)。
显然F是递增的。
则在上面的算法中,要找最大的d[j]时,只要在F中用二分查找法
找到满足j<i且F[d[j]]=aj<ai的最大的j。(此时F[d[j]+1] > a[i]。)
然后将F[d[j]+1]置为ai,即F[d[i]] = a[i]。
注意这里咯,是不是d[i] = max{d[j]+1}了!
有关证明参考这里(http://www.bccn.net/Article/kfyy/vc/jszl/200709/6258.html)。
step 2
于是,去掉数组d
记F[k]为长度为k的最长子序列的最后一个数最小可以是多少。
下面引用两个别人的代码,本质一样:



在二分查找时,一直更新F内容,设此时b的总长度为k.
若1. a[i] >= F[k], 则F[k+1] = a[i];
若2. a[i] < F[k], 则在F[1..k]中用二分搜索大于a[i]的最小值,返回其位置pos,
然后更新b[pos]=a[i]。


在计算d(i)时,在数组F中用二分查找法找到满足j<i且F[d(j)]=aj<ai的最大的j,并将F[d[j]+1]置为ai。

    Java:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值