算法导论——动态规划:最长公共子序列(LCS)

package org.loda.dynamic;

import org.junit.Test;

/**
 * 
 * @ClassName: LCS
 * @Description: 最长公共子序列
 * 
 *               动态规划问题
 * 
 * @author minjun
 * @date 2015年5月19日 上午10:48:57
 * 
 */
public class LCS {

	@Test
	public void lcs() {
		String a = "BDCABA";
		String b = "ABCBDAB";

		// 利用二维数组记录最长公共子序列
		String[][] sub = new String[a.length() + 1][b.length() + 1];

		// 求a,b最长公共子序列长度
		int len = lcs(a, b, sub);

		System.out.println(a + "和" + b + "的最长公共子序列长度为:" + len);

		// 打印获取最长子序列的回溯轨迹
		printRoute(a, b, sub);

		// 获取最长公共子序列
		String lcs = requireLCS(a, b, sub);

		System.out.println("a,b串的其中一个最长公共子序列为:" + lcs);
	}

	private void printRoute(String a, String b, String[][] sub) {
		for (int i = 1; i <= a.length(); i++) {
			for (int j = 1; j <= b.length(); j++) {
				System.out.print(sub[i][j] + "\t");
			}
			System.out.println();
		}
	}

	private String requireLCS(String a, String b, String[][] sub) {
		StringBuilder sb = new StringBuilder();
		int i = a.length();
		int j = b.length();
		//按照标记的箭头回溯路径,并记录当箭头为↖的字符(属于公共子序列的字符)
		while (i > 0 && j > 0) {
			if ("↖".equals(sub[i][j])) {
				sb.append(a.charAt(i - 1));
				i--;
				j--;
			} else if ("←".equals(sub[i][j])) {
				j--;
			} else if ("↑".equals(sub[i][j])) {
				i--;
			}
		}
		return sb.reverse().toString();
	}

	private int lcs(String a, String b, String[][] sub) {
		int lenA = a.length();
		int lenB = b.length();

		// c[i][j]表示子序列a1->ai和子序列b1->bj的最长公共子序列长度
		// 并且在c矩阵中,a字符串作为竖排字符串,b作为横排字符串,a的长度作为行数,b的长度作为列数
		int[][] c = new int[lenA + 1][lenB + 1];

		// c[0][x]和c[x][0]表示有一个子序列长度为0,那么两个的公共子序列长度必然也是0
		// 由于java中整形数组中元素默认值就是0,所以可以省略,这里为了描述算法思想,手动设置为0
		for (int i = 0; i < lenA + 1; i++) {
			c[i][0] = 0;
		}

		for (int j = 0; j < lenB + 1; j++) {
			c[0][j] = 0;
		}

		for (int i = 1; i <= lenA; i++) {
			// 获取字符串a的第i个元素
			char chA = a.charAt(i - 1);
			for (int j = 1; j <= lenB; j++) {
				// 获取字符串b
				char chB = b.charAt(j - 1);

				//利用算法导论中的公式求解
				if (chA == chB) {
					c[i][j] = c[i - 1][j - 1] + 1;
					sub[i][j] = "↖";
				} else {
					if (c[i - 1][j] <= c[i][j - 1]) {
						c[i][j] = c[i][j - 1];
						sub[i][j] = "←";
					} else {
						c[i][j] = c[i - 1][j];
						sub[i][j] = "↑";
					}
				}
			}
		}

		return c[lenA][lenB];
	}
}


输出内容:

BDCABA和ABCBDAB的最长公共子序列长度为:4
←	↖	←	↖	←	←	↖	
←	↑	←	←	↖	←	←	
←	↑	↖	←	←	←	←	
↖	←	↑	←	←	↖	←	
↑	↖	←	↖	←	←	↖	
↖	↑	←	↑	←	↖	←	
a,b串的其中一个最长公共子序列为:BCBA



转载于:https://my.oschina.net/u/1378920/blog/416669

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值