最长公共子序列问题

子序列

X=(A,B,C,B,D,B)

Z=(B,C,D,B)是X的子序例

W=(B,D,A)不是X的子序例

 

公共子序列

Z是序列X与Y的公共子序列如果Z是X的子序 也是Y的子序列。

 

最长公共子序列(LCS)问题

输入:X = (x1,x2,...,xn),Y = (y1,y2,...ym)

输出:Z = X与Y的最长公共子序列

 

蛮力法

枚举X的每个子序列Z

检查Z是否为Y的子序列

T(n)=O(m2^{n})

-----------------------------------------------------分界线,下面开始介绍动态规划求解算法-------------------------------------------------------

第i前缀:

设X=(x1,x2, ..., x n)是一个序列,X的第i前缀Xi 是一个序列,定义为Xi=(x1,...,xi)

例. X=(A, B, D, C, A), X1=(A), X2=(A, B), X3=(A, B, D)

 

优化子结构

 

 

算法结构

算法SimpleLCS(X,Y)

输入:X = (x1,x2,...,xn),Y = (y1,y2,...ym)

输出:X,Y的最长公共子序列

1. If m=0 或 n=0  Then   输出空串,算法结束;

2. If  xn=ym Then

3.          输出SimpleLCS(Xn-1,Ym-1)+<xn>;

4. Else

5.           Z1=SimpleLCS(Xn-1,Y);

6.           Z2=SimpleLCS(X,Ym-1);

7.          输出Z1,Z2中较长者;

 

分析可得子问题具有重叠性

上图理解:知道下层所有子序列,就可以推断出上层子序列

 

LCS长度的递归方程

(C[i, j] = Xi与Yj 的LCS的长度 )

C[i, j] = 0                                      if i=0 或j=0

C[i, j] = C[i-1, j-1] + 1                  if i, j>0 and xi = yj

C[i, j] = Max(C[i, j-1], C[i-1, j])   if i, j>0 and xi \neq yj

 

基本思想

知道上图中蓝色的三项,通过对第i,j号元素的比较就可以求出C[i,j]

 

计算过程

上图中第一行和第一列的元素可以通过两序列率先求得,然后依照上面公式顺序求出各行的长度

 

伪代码

 

Java实现


public class LCS {
	public static void main(String[] args) {
		char[] a= {'a','b','v','s','g','s','f'};
		char[] b= {'a','v','g','s','g','s'};
		getLCS(a, b);
	}
	
	public static char[] getLCS(char[] a,char[] b) {
		
		int m=a.length,n=b.length;
		char[] result0 = {};
		if(m==0||n==0) {
			return result0;
		}
		
		int [][] c=new int[m][n];
		int [][] direction=new int[m][n];
		//初始化第一行
		int i=0;
		while(a[0]!=b[i]&&i<=n) {
			c[0][i]=0;
			i++;
		}
		for (int j=i;j<n;j++) {
			c[0][j]=1;
		}
		
		//初始化第一列
		i=0;
		while(b[0]!=a[i]&&i<=m) {
			c[i][0]=0;
			i++;
		}
		for (int j=i;j<m;j++) {
			c[j][0]=1;
		}
				
		for(i=1;i<m;i++) {
			for(int j=1;j<n;j++) {
				if(a[i]==b[j]) {
					c[i][j]=c[i-1][j-1]+1;
					direction[i][j]=0;//表示指向左上方
				}else if(c[i-1][j]>c[i][j-1]){
					c[i][j]=c[i-1][j];
					direction[i][j]=1;//表示指向上方
				}else {
					c[i][j]=c[i][j-1];//表示指向左方
				}
			}
		}
		for(i=0;i<m;i++) {
			for(int j=0;j<n;j++) {
				System.out.print(c[i][j]+" ");
			}
			System.out.println();
		}
		
		System.out.println("----------------------------");
		printLCS(a, direction, m, n);
		
		
		return null;
	}
	
	public static void printLCS(char[] a ,int[][] direction,int m,int n) {
		if(m==0||n==0) {
			return;
		}
		if(direction[m-1][n-1]==0){
			System.out.println(a[m-1]);
			printLCS(a, direction, m-1, n-1);
		}
		if(direction[m-1][n-1]==1) {
			printLCS(a, direction, m-1, n);
		}
		if(direction[m-1][n-1]==-1) {
			printLCS(a, direction, m, n-1);
		}
		
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值