动态规划之LCS

/**

 * 动态规划之最长公共子序列问题:给定一个子序列 X = <x1,x2,...xm>,另一个子序列Z = <z1,z2,...zk>是X的子序列,如果存在X的一个严格递增下标序列

 * <i1,i2,...ik>,使得对于所有j = 1,2,...k,有xij = zj。如Z=<B,C,D,B>是X=<A,B,C,B,D,A,B>子序列,对应下标i = <2,3,5,7>

 * LCS问题:给定序列X=<x1,x2,...xn>和Y=<y1,y2...ym>,找出X和Y的最长公共子序列

 * 设Z<z1,z2,...zk>是X和Y的任一个最长公共子序列,那么:

 *   1) 若xn = ym,则zk = xn = ym 而且<z1,z2,...zk-1>是Xn-1和Ym-1的最长公共子序列

 *   2) 若xn != ym 则zk!=xn 蕴含Z是Xn-1和Y的一个LCS

 *   3) 若ym != xn 则zk!= ym蕴含Z是x和Ym-1的一个LCS

 *   

 *   基于以上结论,可以得出一个递归解:

 *   定义c[i][j]是序列Xi和Yj的LCS的长度则:

 *              -->  0     if i = 0 或者 j = 0  

 *   c[i][j] =  -->  c[i-1][j-1] + 1   如果i>0,j>0,xi=yj

 *   -->  max{c[i-1][j],c[i][j-1]} 如果i>0,j>0,xi != yj

 * @author song 

 *                

 */

 

	/**
	 * 计算x和y的公共子序列长度表
	 * @param x
	 * @param y
	 * @return
	 */
	public static int[][] calculateCommonSubsequenceNum(char[] x, char[] y){
		int n = x.length;
		int m = y.length;
		//记录各个LCS长度的二维表
		int[][] c = new int[n+1][m+1];
		//初始化:c[i][j] = 0 当i=0 or j=0
		for(int i = 0; i <= m; ++i)
			c[0][i] = 0;
		for(int j = 0; j <= n; ++j)
			c[j][0] = 0;
		//注意c[1][1]记录的是 x[0]和y[0]的公共子序列长度,因为x和y的第一个元素不是x[1]和y[1],而是x[0]和y[0]
		for( int i = 1; i <= n; ++i){
			for(int j = 1; j <= m; ++j){
				if( x[i-1] == y[j-1] )
					c[i][j] = c[i-1][j-1] + 1;
				else{
					c[i][j] = Math.max(c[i-1][j], c[i][j-1]);
				}
			}
		}
		return c;
	}
  /**
	 * 找出一个最长公共子序列
	 * 找出LCS依赖于 {@link #calculateCommonSubsequenceNum(char[], char[])}的LCS长度序列表
	 * 根据最初分析的三个结论
	 *    1)若Z是X和Y的LCS,若xn=ym,则zk = xn = ym即X和Y的最后一个字符肯定出现在LCS中
	 *    2)若xn != ym,则Z肯定是  Xn-1和Ym的LCS 以及 Xn和Ym-1的LCS中更长得那一个序列,也就是说当xn!=ym时,可以转化为找  Xn-1和Ym的LCS/ Xn和Ym-1的LCS
	 *         更进一步,若c[n-1][m] > c[n][m-1],找Xn-1和Ym的LCS即可,因为他更长,反之找Xn和Ym-1的LCS
	 *  因此,从后向前扫描x和y序列,碰到相同则将该字符放入LCS,
	 *    若不相同,则比较c[n-1][m] > c[n][m-1],根据他们的值比较xn-1和ym  或者  xn和ym-1
	 *  
	 * @param x
	 * @param y
	 * @return
	 */
	public static List<Character> findLongestLCS(char[] x,char[] y){
		List<Character> result = new LinkedList<Character>();
		int c[][] = calculateCommonSubsequenceNum(x, y);
		int n = x.length - 1;
		int m = y.length - 1;
		while( n >= 0 && m >= 0){
			if(x[n] == y[m]){
				result.add(0, x[n]);
				n--;m--;
			}else{
				if(c[n][m+1] > c[n+1][m])
					n--;
				else
					m--;
			}
		}
		
		return result;
		
	}

 测试代码:

public static void main(String[] args) {
		char[] x = "10010101".toCharArray();
		char[] y = "010110110".toCharArray();
		List<Character> r = findLongestLCS(x, y);
		System.out.println("The longest LCS:" + r.toString());
	}

 result:The longest LCS:[0, 1, 0, 1, 0, 1]

你可能感兴趣的:(动态规划)