最长序列

最长单调子序列lis

        若a序列<a[1],a[2],…,a[n]>删去其中若干个元素后与b序列完全相同,则称b是a的子序列。
    如果存在一个子序列L=<a[k1],a[k2],…,a[km]>,其中k1 < k2 < … < km且aK1 < ak2 < … < akm,那么序列L是一个单调递增子序列,如果其中的m最大,那么该序列L就是序列a的最长递增子序列。最长递增子序列,英文全称为Longest Increasing Subsequence,缩写为lis。
     设dp[i]表示a序列中以ai为末元素的最长递增子序列的长度。则有如下的动态规划状态转移方程:
     dp[i] = max{dp[j]} + 1     (j<i并且a[i]>a[j])
     初始dp[i]=1,因为这个子序列中至少有a[i]1个元素。


【例题】最长递增子序列

所谓子序列,就是在原序列里删掉若干个元素后剩下的序列,以字符串"abcdefg"为例子,去掉bde得到子序列"acfg" 现在的问题是,给你一个数字序列,你要求出它最长的单调递增子序列。

【输入格式】	
 	 第一行是n(1<=n<=10000),下一行是n个比30000小的非负整数
    【输出格式】
 	最长的单调递增子序列的长度
    【样例输入】	
 	5
	1 10 4 9 7
    【样例输出】
	3	

最长单调子序列属于线性动态规划。在上面的题目中,如果需要我们输出最长上升子序列中的每个元素,我们怎么办呢?
    我们可以定义一个fa[i]数组,存储当前子序列中a[i]的前一个元素。最后递归反向输出。
例如输入:
13
7 9 16 38 24 37 18 44 19 21 22 63 15
输出:
8
7 9 16 18 19 21 22 63

最长下降子序列可转化为反向地求最长上升子序列。



二分法优化最长单调子序列

用二分法优化最长单调子序列,可快速获得子序列的长度(不是状态)。
二分法优化的最长上升子序列:

dp表示长度为i的上升子序列最后一个数最小是多少。显然数组dp是单增的。
    读到一个新的数x后,找到某个i使得x>dp[i]且x<=dp[i+1],于是用x去更新dp[i+1];特别地,如果所有的dp[i]都小于x,则增加dp的长度。
    最后看dp数组有多长就行了。
    由于dp单增,所以查找i时可以用二分查找,因此时间复杂度为O(nlogn)。
    举个例子,假如序列为 3 2 8 6 7 4 5 7 3,则dp数组的变化过程如下:

    3
    2
    2 8
    2 6
    2 6 7
    2 4 7
    2 4 5
    2 4 5 7
    2 3 5 7

    最后,f的长度达到4,因此答案为4。
    注意,最后的f数组不一定是最长上升子序列的一个方案。

 

最长公共子序列lcs

        一个数列 ,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则称为已知序列的最长公共子序列。
    最长公共子序列英文全称The longest common subsequence,简称LCS。最长公共子序列的问题常用于解决字符串的相似度,是一个非常实用的算法,此算法是我们的必备基本功。

    设序列X={x1, x2, ..., xm} 和 Y={y1, y2, ..., yn}的最长公共子序列为Z={z1, z2, ..., zk},则:
    (1) 若xm=yn,则zk=xm=yn,且zk-1是xm-1和yn-1的最长公共子序列;
    (2) 若xm≠yn 且 zk≠xm,则Z是xm-1和Y的最长公共子序列;
    (3) 若xm≠yn 且 zk≠yn,则Z是X和yn-1和Y的最长公共子序列。

    由此可见,2个序列的最长公共子序列包含了这2个序列的前缀的最长公共子序列。因此最长公共子序列具有最优子结构性质。
    用dp[i][j]记录最长公共子序列的长度。其中,Xi={x1, x2, ..., xi} , Yj={y1, y2, ..., yj}。当i=0或j=0时,空序列是Xi和Yj的最长公共子序列。此时,dp[i][j]=0。状态转移方程如下:

代码如下:
计算最长公共子序列的长度:
void lcslength(char x[],char y[], int m,int n){
	int L[m][n],i,j;
	for(i=0;i<=m;i++) L[i][0]=0;
	for(i=0;i<=n;i++) L[0][i]=0; 
	for(i=1;i<=m;i++)
	    for(j=1;j<=n;j++){
			if(x[i]==y[j]) L[i][j] = L[i-1][j-1] + 1;
			  else L[i][j] = max{ L[i-1][j], L[i][j-1] }; 
	    }
	return L[m][n];
}


【例题】最长公共子序列

一个给定序列的子序列是在该序列中删去若干元素后得到的序列。确切地说,若给定序列X=《x1,x2,……,xm》,则另一序列Z=《z1,z2,……,zk》是X的子序列是指存在一个严格递增的下标序列 《i1,i2,……,ik》,使得对于所有j=1,2,……,k有: 
Zj=Xij
    例如,序列Z=《B,C,D,B》是序列X=《A,B,C,B,D,A,B》的子序列,相应的递增下标序列为《2,3,5,7》。给定两个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。例如,若X=《A,B,C,B,D,A,B》和Y=《B,D,C,A,B,A》,则序列《B,C,A》是X和Y的一个公共子序列,序列《B,C,B,A》也是X和Y的一个公共子序列。而且,后者是X和Y的一个最长公共子序列,因为X和Y没有长度大于4的公共子序列。 
    给定两个序列X=《x1,x2,……,xm》和Y=《y1,y2,……,yn》,要求找出X和Y的一个最长公共子序列。 

【输入格式】	
 	 共有两行,每行为一个由大写字母构成的长度不超过200的字符串,表示序列X和Y。
    【输出格式】
 	一个非负整数,表示所求得的最长公共子序列的长度,若不存在公共子序列,则仅输出一个整数0。
    【样例输入】	
 	ABCBDAB
	BDCABA
    【样例输出】
	4	

最长公共子序列的长度很容易求出,那我们如何求出最长公共子序列呢?
例如字符串bacdbd和字符串dbcbadb,我们利用下图来说明:

下表中的数据为dp[i][j]的值:

从表中找出最长公共子序列的方法:
   (1) 从(m,n)到(0,0);
   (2) 若当前格与左边一格相同,则画“←”;
       若当前格与上边一格相同,则画“↑”;
       若以上两者都不符合,从当前格到左上格画坐上箭头。
   (3) 从当前格向箭头方向前进一格,对此格进行
   (4) 从(m,n)到(0,0)的不同路径中,“  ”相对应的格的元素构成最长公共子序列。

对于上面的例题,如果输入:
ABCBDAB
BDCABA
输出:
4
BCBA

如果只需要计算最长公共子序列的长度,则算法的空间需求可大大减少。事实上,在计算dp[i][j]时,只用到数组dp的第i行和第i-1行。因此,用2行的数组空间就可以计算出最长公共子序列的长度。进一步的分析还可将空间需求减至O(min(m,n))。
思考题:如何对程序进行改正,作为思考题。

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值