问题介绍

给定一个序列$X=$,另一个序列$Z=$满足如下条件时称为X的子序列:存在一个严格递增的X的下标序列${i_1,i_2,...,i_k}$,对所有的$j=1,2,...,k$满足$x_{i_j}=z_j.$

给定两个序列$X$和$Y$,如果$Z$同时是$X$和$Y$的子序列,则称$Z$是$X$和$Y$的公共子序列。最长公共子序列(LCS)问题指的是:求解两个序列$X$和$Y$的长度最长的公共子序列。例如,序列$X=\{A,B,C,B,D,A,B\}$和$Y=\{B,D,C,A,B,A\}$的最长公共子序列为$\{B,C,B,A\}$,长度为4。

本文将具体阐释如何用动态规划法(Dynamic Programming)来求解最长公共子序列(LCS)问题。

算法分析

1. LCS的子结构

给定一个序列$X=$,对$i=0,1,...,m$,定义$X$的第i前缀为$X_i=$,其中$X_0$为空序列。

(LCS的子结构)令$X=$和$Y=$为两个序列,$Z=$为$X$和$Y$的任意LCS,则:

如果$x_m=y_n,$则$z_k=x_m=y_n$且$Z_{k-1}$是$X_{m-1}$和$Y_{n-1}$的一个LCS。

如果$x_m\neq y_n,$则$z_k \neq x_m$意味着$Z_{k-1}$是$X_{m-1}$和$Y$的一个LCS。

如果$x_m\neq y_n,$则$z_k\neq y_n$且$Z_{k-1}$是$X$和$Y_{n-1}$的一个LCS。

2. 构造递归解

在求$X=$和$Y=$的一个LCS时,需要求解一个或两个子问题:如果$x_m=y_n$,应求解$X_{m-1}$和$Y_{n-1}$的一个LCS,再将$x_m=y_n$追加到这个LCS的末尾,就得到$X$和$Y$的一个LCS;如果$x_m\neq y_n$,需求解$X_{m-1}$和$Y$的一个LCS与$X$和$Y_{n-1}$的一个LCS,两个LCS较长者即为$X$和$Y$的一个LCS。当然,可以看出,LCS问题容易出现重叠子问题,这时候,就需要用动态规划法来解决。

定义$c[i,j]$表示$X_i$和$Y_j$的LCS的长度。如果$i=0$或$j=0$,则$c[i,j]=0.$利用LCS的子结构,可以得到如下公式:

$$

c[i,j]=\left\{

\begin{array}{lr}

0,\qquad 若i=0或j=0\\

c[i-1, j-1]+1,\qquad 若i,j>0且x_i=y_j\\

\max(c[i, j-1], c[i-1, j]),\qquad 若i,j>0且x_i\neq y_j

\end{array}

\right.

$$

3. 计算LCS的长度

计算LCS长度的伪代码为LCS-LENGTH. 过程LCS-LENGTH接受两个子序列$X=$和$Y=$为输入。它将$c[i, j]$的值保存在表$c$中,同时,维护一个表$b$,帮助构造最优解。过程LCS-LENGTH的伪代码如下:

LCS-LENGTH(X, Y):

m = X.length

n = Y.length

let b[1...m, 1...n] and c[0...m, 0...n] be new table

for i = 1 to m

c[i, 0] = 0

for j = 1 to n

c[0, j] = 0

for i = 1 to m

for j = 1 to n

if x[i] == y[j]

c[i,j] = c[i-1, j-1]+1

b[i,j] = 'diag'

elseif c[i-1, j] >= c[i, j-1]

c[i,j] = c[i-1, j]

b[i,j] = 'up'

else

c[i,j] = c[i, j-1]

b[i,j] = 'left'

return c and b

4. 寻找LCS

为了寻找$X$和$Y$的一个LCS, 我们需要用到LCS-LENGTH过程中的表$b$,只需要简单地从$b[m, n]$开始,并按箭头方向追踪下去即可。当在表项$b[i,j]$中遇到一个'diag'时,意味着$x_i=y_j$是LCS的一个元素。按照这种方法,我们可以按逆序依次构造出LCS的所有元素。伪代码PRINT-LCS如下:

PRINT-LCS(b, X, i, j):

if i == 0 or j == 0

return

if b[i,j] == 'diag'

PRINT-LCS(b, X, i-1, j-1)

print x[i]

elseif b[i,j] == 'up':

PRINT-LCS(b, X, i-1, j)

else

PRINT-LCS(b, X, i, j-1)

程序实现

有了以上对LCS问题的算法分析,我们不难写出具体的程序来实现它。下面将会给出Python代码和Java代码,供读者参考。

完整的Python代码如下:

import numpy as np

# using dynamic programming to solve LCS problem

# parameters: X,Y -> list

def LCS_LENGTH(X, Y):

m = len(X) # length of X

n = len(Y) # length of Y

# create two tables, b for directions, c for solution of sub-problem

b = np.array([[None]*(n+1)]*(m+1))

c = np.array([[0]*(n+1)]*(m+1))

# use DP to sole LCS problem

for i in range(1, m+1):

for j in range(1, n+1):

if X[i-1] == Y[j-1]:

c[i,j] = c[i-1,j-1]+1

b[i,j] = 'diag'

elif c[i-1,j] >= c[i, j-1]:

c[i,j] = c[i-1,j]

b[i,j] = 'up'

else:

c[i,j] = c[i,j-1]

b[i,j] = 'left'

#print(b)

#print(c)

return b,c

# print longest common subsequence of X and Y

def print_LCS(b, X, i, j):

if i == 0 or j == 0:

return None

if b[i,j] == 'diag':

print_LCS(b, X, i-1, j-1)

print(X[i-1], end=' ')

elif b[i,j] == 'up':

print_LCS(b, X, i-1, j)

else:

print_LCS(b, X, i, j-1)

X = 'conservatives'

Y = 'breather'

b,c = LCS_LENGTH(X,Y)

print_LCS(b, X, len(X), len(Y))

输出结果如下:

e a t e

完整的Java代码如下:

package DP_example;

import java.util.Arrays;

import java.util.List;

public class LCS {

// 主函数

public static void main(String[] args) {

// 两个序列X和Y

List X = Arrays.asList("A","B","C","B","D","A","B");

List Y = Arrays.asList("B","D","C","A","B","A");

int m = X.size(); //X的长度

int n = Y.size(); // Y的长度

String[][] b = LCS_length(X, Y); //获取维护表b的值

print_LCS(b, X, m, n); // 输出LCS

}

/*

函数LCS_length:获取维护表b的值

传入参数: 两个序列X和Y

返回值: 维护表b

*/

public static String[][] LCS_length(List X, List Y){

int m = X.size(); //X的长度

int n = Y.size(); // Y的长度

int[][] c = new int[m+1][n+1];

String[][] b = new String[m+1][n+1];

// 对表b和表c进行初始化

for(int i=1; i

for(int j=1; j

c[i][j] = 0;

b[i][j] = "";

}

}

// 利用自底向上的动态规划法获取b和c的值

for(int i=1; i

for(int j=1; j

if(X.get(i-1) == Y.get(j-1)){

c[i][j] = c[i-1][j-1]+1;

b[i][j] = "diag";

}

else if(c[i-1][j] >= c[i][j-1]){

c[i][j] = c[i-1][j];

b[i][j] = "up";

}

else{

c[i][j] = c[i][j-1];

b[i][j] = "left";

}

}

}

return b;

}

// 输出最长公共子序列

public static int print_LCS(String[][] b, List X, int i, int j){

if(i == 0 || j == 0)

return 0;

if(b[i][j].equals("diag")){

print_LCS(b, X, i-1, j-1);

System.out.print(X.get(i-1)+" ");

}

else if(b[i][j].equals("up"))

print_LCS(b, X, i-1, j);

else

print_LCS(b, X, i, j-1);

return 1;

}

}

输出结果如下:

B C B A

参考文献

注意:本人现已开通两个微信公众号: 因为Python(微信号为:python_math)以及轻松学会Python爬虫(微信号为:easy_web_scrape), 欢迎大家关注哦~~

lcs问题java_动态规划法(十)最长公共子序列(LCS)问题相关推荐

  1. 动态规划算法解最长公共子序列LCS问题

    动态规划算法解LCS问题 作者 July 二零一零年十二月三十一日 本文参考:微软面试100题系列V0.1版第19.56题.算法导论.维基百科. 第一部分.什么是动态规划算法 ok,咱们先来了解下什么 ...

  2. 程序员编程艺术第十一章:最长公共子序列(LCS)问题

    程序员编程艺术第十一章:最长公共子序列(LCS)问题 0.前言 程序员编程艺术系列重新开始创作了(前十章,请参考程序员编程艺术第一~十章集锦与总结).回顾之前的前十章,有些代码是值得商榷的,因当时的代 ...

  3. 计算机算法设计与分析 动态规划 实验报告,动态规划法解最长公共子序列(计算机算法设计与分析实验报告).doc...

    动态规划法解最长公共子序列(计算机算法设计与分析实验报告) 实报 告 实验名称:任课教师::姓 名:完成日期:二.主要实验内容及要求: 要求按动态规划法原理求解问题: 要求交互输入两个序列数据: 要求 ...

  4. 相似度:最长公共子序列--LCS

    一.概念 1.子序列 一个特定序列的子序列就是将给定序列中零个或多个元素去掉后得到的结果(不改变元素间相对次序).如序列[A,B,C,B,D,A,B]的子序列有:[A,B],[B,C,A],[A,D, ...

  5. 动态规划之最长公共子序列(LCS)

    最长公共子序列(LCS,Longest Common Subsequence).其定义是,一个序列 S ,如果分别是两个或多个已知序列的子序列,且是所有符合此条件序列中最长的,则 S 称为已知序列的最 ...

  6. 算法之最长公共子序列(LCS)问题

    算法课上老师留的作业,最长公共子序列LCS(Longest Common Subsequence)问题,首先看到这个问题感觉有点复杂,和最长公共子串不同,公共子序列并不要求元素相邻,看起来只有穷举才能 ...

  7. 最长公共子序列php,动态规划(最长公共子序列LCS)

    概念 求解决策过程最优化的结果 (可能有多个) 把多阶段过程转化为一系列单阶段过程,利用各阶段之间的关系,逐个求解 计算过程中会把结果都记录下,最终结果在记录中找到. 举例 求两个字符串的最长公共子序 ...

  8. python实现求解最长公共子序列LCS问题

    在实现论文<Automatically Generating Models for Botnet Detection>论文的算法中,用到了一个The longest commom subs ...

  9. 算法导论-----最长公共子序列LCS(动态规划)

    目录 一.概念梳理 二.最长公共子序列解决方案 方案1:蛮力搜索策略 方案2:动态规划策略 三.C代码实现 实现1 实现2(空间优化) 一.概念梳理   1. 子序列(subsequence): 一个 ...

  10. 动态规划表格法解决最长公共子序列(LCS)问题

    3.5 最长公共子序列(LCS) 前言:图片是博主自己画的,转载请注明出处哦 3.5.1 问题描述 最长公共子序列(Longest Common Subseuence,LCS)问题:给定两个字符串,求 ...

最新文章

  1. 2022-02-09
  2. [设计模式][c++]状态切换模式
  3. (Spring程序开发)简介程序开发步骤配置文件相关API
  4. 神经网络与深度学习——TensorFlow2.0实战(笔记)(四)(python函数)
  5. Python crypto模块实现RSA和AES加密解密
  6. np.roll的作用
  7. 极速pdf编辑器的水印如何去掉_如何去掉PDF右下角的全能扫描王水印
  8. 怎么升级Android Studio版本,Android studio 2 版本升级 Android studio 3 版本注意事项
  9. 机器学习笔记 - 什么是图神经网络?
  10. 国内最好的B端设计资源,都在这里了
  11. 使用SecureCRT登录本机cygwin【转】
  12. Android 高仿腾讯旗下app的 皮肤加载技术
  13. windows server 2016 安装openssh
  14. win10下 phantomjs下载安装与使用
  15. 处女作真人语音计算器上线了
  16. Android 9.0 SystemUI 下拉状态栏快捷开关
  17. MySQL内置函数中的日期和时间函数详解
  18. PID:智能小车入门(位置式和增量式)
  19. 断言(Assertion)
  20. 吐血整理-周志华演讲合集

热门文章

  1. 一文详解结构光发展简史
  2. 长沙市取消职称英语和计算机,哪些城市已取消职称英语考试?
  3. FFmpeg[14] - ffbuild/common.mak:173: *** missing separator. Stop.
  4. 明源售楼系统技术解析 折扣管理(二)
  5. 10款主流的软件测试工具,你用过吗
  6. QQ自由幻想刺客的属性点
  7. Google Test使用简介
  8. 01. Linux 简介
  9. matlab保存bln文件,气象万千(冯锦明课题组)-软件程序
  10. 基于Python绘制一个摸鱼倒计时界面