最长公共子序列

问题描述:一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最长公共子序列。而最长公共子串(要求连续)和最长公共子序列是不同的。

LCS的最优子结构

设 X={x1, x2, x3, x4,..., xm} 和 Y={y1, y2, y3, y4,..., yn} 为两个序列,并设 Z={z1, z2, z3, z4,..., zk} 为 X 与 Y 的任意一个LCS,则:

  1. 若xm=yn,则zk=xm=yn 且Zk-1是Xm-1和Yn-1的一个LCS
  2. 若xm≠yn且zk≠xm ,则Z是Xm-1和Y的一个LCS
  3. 若xm≠yn且zk≠yn ,则Z是X和Yn-1的一个LCS

重叠子问题

由LCS问题的最优子结构可得递归式


满足了动态规划的两个条件之后,该问题可以采用备忘录(也叫打表)思想进行求解,下面举例说明实际操作过程:

设有两个序列x="ABCBDAB"(m=7)和y="BDCABA"(n=6)

步骤一:打表,按行填充二维数组,填充规则参照根据最优子结构得出的递归式(其中第0行第0列作为哨兵可全部填0),填充结果如下


步骤二:从数组的最右下角开始,每遇到朝左上的箭头,则输出字符,最后可得LCS={BCBA}

代码实现如下:

public class Solution {
	enum Direction {
		L, U, UL, DEFAULT
	};

	Item NULL = new Item(0, Direction.DEFAULT);

	public String LCS(String x, String y) {
		//需要第0列作哨兵,所以需要长度+1
		int lenx = x.length() + 1, leny = y.length() + 1;
		Item c[][] = new Item[lenx][leny];
		for (int i = 0; i < lenx; i++) {
			c[i][0] = NULL;
		}
		for (int j = 0; j < leny; j++) {
			c[0][j] = NULL;
		}
		for (int i = 1; i < lenx; i++) {
			for (int j = 1; j < leny; j++) {
				if (x.charAt(i - 1) == y.charAt(j - 1)) {
					c[i][j] = new Item(c[i - 1][j - 1].val + 1, Direction.UL);
				} else {
					if (c[i][j - 1].val > c[i - 1][j].val) {
						c[i][j] = new Item(c[i][j - 1].val, Direction.L);
					} else {
						c[i][j] = new Item(c[i - 1][j].val, Direction.U);
					}
				}
			}
		}
		StringBuilder result = new StringBuilder(c[lenx - 1][leny - 1].val);
		for (int i = lenx - 1; i >= 0;) {
			for (int j = leny - 1; j >= 0;) {
				switch (c[i][j].direct) {
				case L:
					j -= 1;
					break;
				case U:
					i -= 1;
					break;
				case UL:
					result.insert(0, x.charAt(i - 1));
					i -= 1;
					j -= 1;
					break;
				case DEFAULT:
				default:
					return result.toString();
				}
			}
		}
		return result.toString();
	}

	class Item {
		int val;
		Direction direct;

		Item(int v, Direction d) {
			this.val = v;
			this.direct = d;
		}
	}
}

另外,需要说明一点,两个字符串的LCS可能存在多个,那么允许lcs(x,y)和lcs(y,x)返回的结果不一样,只需要保证长度相等即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值