算法导论读书笔记(18)

算法导论读书笔记(18)

目录

  • 最长公共子序列
    • 步骤1:描述最长公共子序列的特征

    • 步骤2:一个递归解

    • 步骤3:计算LCS的长度

    • 步骤4:构造LCS

  • LCS问题的简单Java实现

最长公共子序列

某给定序列的子序列,就是将给定序列中零个或多个元素去掉后得到的结果。其形式化定义如下:给定一个序列 X = <
x1 , x2 , … , xm
>,另一个序列 Z = < z1 , z2 , …
, zk >,如果 Z 满足如下条件则称 ZX
子序列 (subsequence),即存在一个严格递增的 X 的下标序列 <
i1 , i2 , … , ik
>,对所有 j = 1,2,…, k ,满足 xij =
zj 。给定两个序列 XY ,如果 Z 既是
X 的子序列,也是 Y 的子序列,则称它是 XY
公共子序列

最长公共子序列问题 (longest-common-subsequence problem)就是给定两个序列
X = < x1 , x2 , … ,
xm >和 Y = < y1 ,
y2 , … , yn >,求 X
Y 长度最长的公共子序列。简称LCS问题。下面将展示如何用动态规划方法高效求解LCS问题。

步骤1:描述最长公共子序列的特征

LCS问题符合最优子结构的性质。可以看到,子问题的自然分类对应两个输入序列的“前缀”对。前缀的严格定义如下:给定一个序列 X =
< x1 , x2 , … ,
xm >,对 i = 0,1,…, m ,定义 X 的第
i 前缀为 Xi = < x1 ,
x2 , … , xi >, X0
为空串。

定理 (LCS的最优子结构)

X = <
x1 , x2 , … , xm
>和 Y = < y1 , y2 , … ,
yn >为两个序列, Z = < z1 ,
z2 , … , zk >为 X
Y 的任意LCS。
1. 如果 xm = yn ,则
zk = xm = yn
Zk-1Xm-1
Yn-1 的一个LCS。
2. 如果 xm
yn ,那么 zkxm 意味着
ZXm-1Y 的一个LCS。
3. 如果
xmyn ,那么 zk
yn 意味着 ZX
Yn-1 的一个LCS。

上面的定理说明两个序列的LCS包含两个序列的前缀的LCS。因此,LCS问题满足最优子结构性质。

步骤2:一个递归解

由定理可知,在求 X = < x1 , x2 ,
… , xm >和 Y = < y1 ,
y2 , … , yn
>的一个LCS时,我们需要求解一个或两个子问题。如果 xm = yn
,我们应该求解 Xm-1
Yn-1 的一个LCS。然后将 xm =
yn 追加到这个LCS的末尾,就得到 XY 的一个LCS。如果
xmyn ,我们必须求解两个子问题:求
Xm-1Y 的一个LCS与 X
Yn-1 的一个LCS。两个LCS中长的那个即为 XY
的一个LCS。

可以很容易看出LCS中的重叠子问题。为了求 XY 的一个LCS,我们可能需要求 X
Yn-1 的一个LCS以及 Xm-1
Y 的一个LCS。这几个子问题都包含求解 Xm-1
Yn-1 的LCS的子子问题。

设计LCS问题的递归算法还要建立最优解的递归式。令 c [ i , j ]表示
XiYj 的LCS的长度。如果 i = 0或
j = 0,即一个序列长度为0,那么LCS的长度为0。根据LCS问题的最优子结构性质,可知:

步骤3:计算LCS的长度

过程 LCS-LENGTH 接受两个序列 X = < x1 ,
x2 , … , xm >和 Y = <
y1 , y2 , … , yn
>为输入。它将 c [ i , j ]的值保存在表 c [ 0 ..
m , 0 .. n ],并按 行主次序 (row-major
order)计算表项(即首先由左至右计算 c 的第一行,然后第二行,依此类推)。过程还维护一个表 b [ 1 ..
m , 1 .. n ]帮助构造最优解。 b [ i , j
]指向的表项对应计算 c [ i , j ]时所选择的子问题的最优解。过程返回表 b
和表 cc [ m , n ]保存了 X
Y 的LCS的长度。

LCS-LENGTH(X, Y)
1 m = X.length
2 n = Y.length
3 let b[1..m, 1..n] and c[0..m, 0..n] be new tables
4 for i = 1 to n
5 c[i, 0] = 0
6 for j = 0 to n
7 c[0, j] = 0
8 for i = 1 to m
9 for j = 1 to n
10 if x_i == y_j
11 c[i, j] = c[i - 1, j - 1] + 1
12 b[i, j] = ""
13 elseif c[i - 1, j] >= c[i, j - 1]
14 c[i, j] = c[i - 1, j]
15 b[i, j] = "↑"
16 else
17 c[i, j] = c[i, j - 1]
18 b[i, j] = "←"
19 return c and b

下图显示了 LCS-LENGTH 对输入序列 X = < A , B
, C , B , D , A , B >和
Y = < B , D , C , A , B
, A >生成的结果。过程的运行时间为 Θ ( mn ),因为每个表项的计算时间为
Θ ( 1 )。

步骤4:构造LCS

现在可以用 LCS-LENGTH 返回的表 b 快速构造 X = <
x1 , x2 , … , xm
>和 Y = < y1 , y2 , … ,
yn >的LCS。

PRINT-LCS(b, X, i, j)
1 if i == 0 or j == 0
2 return
3 if b[i, j] == ""
4 PRINT-LCS(b, X, i - 1, j - 1)
5 print x_i
6 elseif b[i, j] == "↑"
7 PRINT-LCS(b, X, i - 1, j)
8 else
9 PRINT-LCS(b, X, i, j - 1)

LCS问题的简单Java实现

参考自http://www.cs.cityu.edu.hk/~lwang/cs5302/LCS.java

private static int[][] lcsLength(String x, String y) {
int m = x.length();
int n = y.length();
int[][] b = new int[m + 1][n + 1];
int[][] c = new int[m + 1][n + 1];
for (int i = 0; i < n; i++)
c[i][0] = 0;
for (int j = 0; j < m; j++)
c[0][j] = 0;
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
if (x.charAt(i - 1) == y.charAt(j - 1)) {
c[i][j] = c[i - 1][j - 1] + 1;
b[i][j] = DIAGONAL;
} 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] = FORWARD;
}
}
}
return b;
}

public static String getLCS(String x, String y) {
int[][] b = lcsLength(x, y);
String lcs = "";
int i = x.length();
int j = y.length();
while (i != 0 && j != 0) {
if (b[i][j] == DIAGONAL) {
lcs = x.charAt(i - 1) + lcs;
i = i - 1;
j = j - 1;
}
if (b[i][j] == UP) {
i = i - 1;
}
if (b[i][j] == FORWARD) {
j = j - 1;
}
}
return lcs;
}

private static final int DIAGONAL = 1;
private static final int UP = 2;
private static final int FORWARD = 3;

算法导论读书笔记(18)

时间: 2024-10-07 05:45:14

算法导论读书笔记(18)的相关文章

算法导论读书笔记之钢条切割问题

算法导论读书笔记之钢条切割问题 巧若拙(欢迎转载,但请注明出处:http://blog.csdn.net/qiaoruozhuo) 给定一段长度为n英寸的钢条和一个价格表 pi (i=1,2, -,n),求切割钢条的方案,使得销售收益rn最大.注意,如果长度为n英寸的钢条价格pn足够大,最优解可能就是完全不需要切割. 若钢条的长度为i,则钢条的价格为Pi,如何对给定长度的钢条进行切割能得到最大收益? 长度i   1   2    3   4     5      6     7     8  

算法导论读书笔记(13)

算法导论读书笔记(13) 目录 红黑树 旋转 插入 情况1 : z 的叔父结点 y 是红色的 情况2 : z 的叔父结点 y 是黑色的,而且 z 是右孩子 情况3 : z 的叔父结点 y 是黑色的,而且 z 是左孩子 删除 情况1 : x 的兄弟 w 是红色的 情况2 : x 的兄弟 w 是黑色的,且 w 的两个孩子都是黑色的 情况3 : x 的兄弟 w 是黑色的, w 的左孩子是红色的,右孩子是黑色的 情况4 : x 的兄弟 w 是黑色的,且 w 的右孩子是红色的 红黑树 红黑树 是一种二叉查

算法导论读书笔记(15) - 红黑树的具体实现

算法导论读书笔记(15) - 红黑树的具体实现 目录 红黑树的简单Java实现 红黑树的简单Java实现 /** * 红黑树 * * 部分代码参考自TreeMap源码 */ public class RedBlackTree<T> { protected TreeNode<T> root = null; private final Comparator<? super T> comparator; private int size = 0; private static

算法导论读书笔记(17)

算法导论读书笔记(17) 目录 动态规划概述 钢条切割 自顶向下的递归实现 使用动态规划解决钢条切割问题 子问题图 重构解 钢条切割问题的简单Java实现 动态规划概述 和分治法一样, 动态规划 (dynamic programming)是通过组合子问题的解而解决整个问题的.分治法是将问题划分成一些独立的子问题,递归地求解各子问题,然后合并子问题的解而得到原问题的解.与此不同,动态规划适用于子问题并不独立的情况,即各子问题包含公共的子子问题.在这种情况下,分治法会重复地求解公共的子子问题.而动态

算法导论读书笔记(16)

算法导论读书笔记(16) 目录 动态顺序统计 检索具有给定排序的元素 确定一个元素的秩 区间树 步骤1:基础数据结构 步骤2:附加信息 步骤3:维护信息 步骤4:设计新操作 动态顺序统计 之前介绍过 顺序统计 的概念.在一个无序的集合中,任意的顺序统计量都可以在 O ( n )时间内找到.而这里我们将介绍如何在 O ( lg n )时间内确定任意的顺序统计量. 下图显示的是一种支持快速顺序统计量操作的数据结构.一棵 顺序统计树 T 通过在红黑树的每个结点中存入附加信息而成.在一个结点 x 内,增

算法导论读书笔记(14) - 二叉查找树的具体实现

算法导论读书笔记(14) - 二叉查找树的具体实现 目录 二叉查找树的简单Java实现 二叉查找树的简单Java实现 /** * 二叉查找树 * 部分代码参考自TreeMap的源码 */ public class BinarySearchTree<T> { protected TreeNode<T> root = null; private final Comparator<? super T> comparator; private int size = 0; pub

算法导论读书笔记-第十四章-数据结构的扩张

算法导论第14章 数据结构的扩张 一些工程应用需要的只是标准数据结构, 但也有许多其他的应用需要对现有数据结构进行少许的创新和改造, 但是只在很少情况下需要创造出全新类型的数据结构, 更经常的是通过存储额外信息的方法来扩张一种标准的数据结构, 然后对这种数据结构编写新的操作来支持所需要的应用. 但是对数据结构的扩张并不总是简单直接的, 因为新的信息必须要能被该数据结构上的常规操作更新和维护. 14.1 动态顺序统计 顺序统计树(order-static tree) : 在红黑树的基础上, 在每个

平摊分析 --- 算法导论读书笔记

我们经常会说一个算法快不快,这个可以由实验得出,也可以通过分析复杂度得出.实验需要大量不同的输入才更全面准确,否则片面地看某个输入下的表现,是比较偏颇的.分析复杂度(通常分析最坏,因为平均涉及输入的概率分布,依靠假设或者实验和经验)有时候并不是一个简单的事,简单的情况是遍历 for(int i = 0; i != n; i++) 的这种情况,显然是O(n)的复杂度.但是一些复杂的情况就比较难办了,举例来说: a.   栈操作:  除了PUSH,POP,添加一个操作叫MULTIPOP. MULTI

字符串匹配问题 ---- 算法导论读书笔记

字符串匹配是一个很常见的问题,可以扩展为模式的识别,解决字符串问题的思想被广泛地应用.介绍四种解决该问题的办法,包括:最朴素的遍历法,Rabin-Karp算法,自动机机匹配,Knuth-Morris-Pratt算法即耳熟能详的KMP. 在一开始,先对时间复杂度做出一个总扩(从大到小):[1]朴素法:O( (n-m+1)m ):[2]Rabin-Karp:预处理:O(m),匹配:最坏O( (n-m+1)m ),但是平均和实际中比这个好得多:[3]自动机:预处理O(m|Σ|),匹配O(n):[4]K