最长公共子序列(LCS)

最长公共子序列LCS问题
       给定2个序列X和Y,当另一序列Z既是X的子序列又是Y的子序列时,称Z是序列X和Y的公共子序列。给定X={x1, x2, …, xm}和Y = {y1, y2, …, yn},请找出X和Y的最长公共子序列。例如:
输入:X=ABCHFGASJKXBD           Y=DBDHASXB
输出:LCS=BHASXB
【分析】

       最长公共子序列LCS问题可用动态规划算法有效解决。而首先我们需要证明的是,使用动态规划算法的两个基本要素,即为什么最长公共子序列LCS问题可以用动态规划算法来解决。

最长公共子序列问题具有最优子结构性质:

       设序列X={x1,x2,x2,……,xm}Y={y1,y2,y3,……,yn},而Z={z1,z2,z3,……,zk}XY的最长公共子序列。则:

xm = yn,则zk = xm = ynZk-1Xm-1 Yn-1的最长公共子序列。

       (证:采用反证法,若zk != xm,那么有{z1,z2,z3,……,zk,xm}是为XY长度为k+1的最长公共子序列,这与ZXY长度为k的最长公共子序列矛盾。

       若Zk-1不是Xm-1Yn-1的最长公共子序列,则Xm-1Yn-1具有长度大于k-1的最长公共子序列,那么加上xm后,XY的最长公共子序列的长度应该大于k,这与ZXY长度为k的最长公共子序列矛盾。)

       若xm != yn 且 zk != xm,则ZXm-1Y的最长公共子序列。

       (证:采用反证法,若Z不是Xm-1Y的最长公共子序列,则Xm-1Y具有长度大于k的最长公共子序列,那么XY的最长公共子序列的长度必定也大于k。这与XY具有长度为k的最长公共子序列Z矛盾。)

xm != yn 且 zk != yn,则ZXYn-1的最长公共子序列。

       (证:采用反证法,证法同上述类似)


      最长公共子序列问题具有子问题重叠性质。

在寻找XY的最长公共子序列时,我们要进行分类讨论:

xm = yn时,我们要找出Xm-1Yn-1的最长公共子序列,在其尾部加上xm就为XY的最长公共子序列;

xm != yn时,我们要找出 Xm-1Y的最长公共子序列和XYn-1的最长公共子序列,再比较两者哪个大,即为XY的最长公共子序列。

       我们可以从中看出,当我们要查找Xm-1Y的最长公共子序列时,同样也要按照上面所说的步骤进行查找,即有可能要继续查找Xm-2YXm-1Yn-1的最长公共子序列;当我们要XYn-1的最长公共子序列时,同样也要按照上面所说的步骤进行查找,即有可能要继续查找Xm-1Yn-1XYn-2的最长公共子序列。在这两个子问题都包含了一个公共子问题,即计算Xm-1Yn-1的最长公共子序列。

 

       建立LCS问题的递推关系,用c[i][j]表示XiYj的最长公共子序列的长度。其中Xi={x1,x2,……,xi}Yj={y1,y2,……,yj}

c[i][j] = 0,   i = j = 0;

c[i][j] = c[i - 1][j - 1] + 1,   xi = yj;

c[i][j] = max{c[i - 1][j], c[i][j - 1]},   xi != yj;

根据递推关系,我们可以写出程序。

【程序】
用java语言编写程序,代码如下:

import java.io.BufferedInputStream;
import java.util.Scanner;

public class LCS {
	public static void main(String[] args) {
		Scanner input = new Scanner(new BufferedInputStream(System.in));
		
		while(input.hasNext()) {
			int m = input.nextInt();
			String sx = input.next();
			char[] x = new char[m + 1];
			for(int i = 1; i < m + 1; i++)
				x[i] = sx.charAt(i - 1);
			
			int n = input.nextInt();
			String sy = input.next();
			char[] y = new char[n + 1];
			for(int i = 1; i < n + 1; i++)
				y[i] = sy.charAt(i - 1);
			
			/*int[][] c = new int[m + 1][n + 1];
			for(int i = 0; i < m + 1; i++)
				for(int j = 0; j < n + 1; j++)
					c[i][j] = -1;
			
			int len = LCS(m, n, x, y, c);
			System.out.println();
			System.out.println(len);*/
			
			int[][] c = new int[m + 1][n + 1];
			int[][] b = new int[m + 1][n + 1];
			LCSlength(m, n, x, y, c, b);
			printLCS(m, n, b, x);
			System.out.println();
			System.out.println(c[m][n]);
		}
	}
	
	/*public static int LCS(int m, int n, char[] x, char[] y, int[][] c) {
		if(m == 0 || n == 0) {
			c[m][n] = 0;
			return 0;
		}
		
		//if(c[m][n] != -1)
			//return c[m][n];
		
		if(x[m] == y[n]) {
			c[m][n] = LCS(m - 1, n - 1, x, y, c) + 1;
			System.out.print(x[m]);
			System.out.println(m + " " + n);
		}
		else {
			int lcs1 = LCS(m, n - 1, x, y, c);
			int lcs2 = LCS(m - 1, n, x, y, c);
			if(lcs1 > lcs2)
				c[m][n] = lcs1;
			else
				c[m][n] = lcs2;
		}
		return c[m][n];
	}*/
	
	public static void LCSlength(int m, int n, char[] x, char[] y, int[][] c, int[][] b) {
		int i, j;
		for(i = 1; i <= m; i++)
			c[i][0] = 0;
		for(j = 1; j <= n; j++)
			c[0][j] = 0;
		
		for(i = 1; i <= m; i++)
			for(j = 1; j <= n; j++) {
				if(x[i] == y[j]) {
					c[i][j] = c[i - 1][j - 1] + 1;
					b[i][j] = 1;
				}
				else {
					if(c[i - 1][j] >= c[i][j - 1]) {
						c[i][j] = c[i - 1][j];
						b[i][j] = 2;
					}
					else {
						c[i][j] = c[i][j - 1];
						b[i][j] = 3;
					}
				}
			}
	}
	
	public static void printLCS(int i, int j, int[][] b, char[] x) {
		if(i == 0 || j == 0)
			return;
		
		if(b[i][j] == 1) {
			printLCS(i - 1, j - 1, b, x);
			System.out.print(x[i]);
		}
		else if(b[i][j] == 2)
			printLCS(i - 1, j, b, x);
		else
			printLCS(i, j - 1, b, x);
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值