最长公共子序列

来源:互联网 发布:盖革计数器 知乎 编辑:程序博客网 时间:2024/04/30 18:34

问题描述:一个序列 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列作哨兵,所以需要长度+1int 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
原创粉丝点击