算法作业:
LCS 问 题
作业要求:设计一个算法求出两个序列的所有 LCS,分析最坏情况,用“会计方法”证明利用 b[i][j]求出所
有 LCS 的算法在最坏情况下时间复杂度为
1、 算法思路:
根据最长公共子序列问题的性质,即经过分解后的子问题具有高度重复性,并且具有最优子结构
性质,采用动态规划法求解问题。设 X={x
1
, x
2
, … , x
n
}, Y={y
1
, y
2
, … , y
m
}, 首先引入二维数组 C[i][j]记
录 X
i
和 Y
j
的 LCS 的长度,定义 C[i][j]如下:
为了构造出 LCS,还需要使用一个二维数组 b[m][n],b[i][j]记录 C[i][j]是通过哪个子问题的值求得
的,以决定搜索的方向,欲求出所有的 LCS,定义数组 b 如下:
设 1-对角线方向;2-向上;3-向左;4-向上或向左
若 X[i]=Y[j],b[i][j] = 1,
若 C[i-1][j]<i][j-1], 则 b[i][j] = 2,
若 C[i-1][j]>[i][j-1], 则 b[i][j] = 3,
若 C[i-1][j]=[i][j-1], 则 b[i][j] = 4,
根据以上辅助数组 C 和 b 的定义,算法首先需要求出这两个数组, C[m][n]中记录的最长公共子序列
的长度,b 中记录了查找子序列元素的搜索方向。
利用 C 和 b 的信息,Find_All_LCS 可以采用回溯法求出所有的 LCS。基本思路如下:使用一个辅助
数组记录每次调用 Find_All_LCS 得到的 LCS 中的元素,每次递归调用一次 Find_All_LCS,进入一个
新的执行层,首先要判断当前处理的两个子序列长度是否大于等于 0 ,若不满足,则该层的递归结束,
返回上一层;然后再判断当前得到的子序列是否等于数组 C 中求出的最长公共子序列长度,若等于,
则说明算法执行到此已经得到一个 LCS,按序输出;若不等于,此时根据数组 b 中记录的搜索方向继
续 搜 索 , 特 别 要 说 明 的 是 , 当 b[i][j]=4 时 , 即 要 向 上 或 向 左 , 需 要 对 这 两 个 方 向 分 别 调 用
Find_All_LCS,保证沿着这两个方向上 LCS 元素不被漏掉,都可以搜索到;若 b[i][j]=1,即沿对角线
方向搜索前进时,此时元素 X[i]为 LCS 中的元素,存放至辅助数组中去,同时将当前已经求得的 LCS
长度增 1,当递归调用 Find_All_LCS 从 b[i][j]=1 处时,需要回溯一步,搜索其它路径上可能为 LCS 中
的元素。当所有的可能路径都已经搜索完,算法结束。
对于某些情况会输出重复的 LCS,这是因为算法在沿不同路径搜索时可能会出现相同的 LCS 序列。
2、 时间复杂度分析
由上述对 Find_All_LCS 算法的分析可知,求出所有的 LCS 实际上是根据搜索的方向信息遍历所有
的路径找出满足条件的元素集合。因此,除求解辅助数组 C 和 b 所用的 O(mn+m+n)的执行时间外,
Find_All_LCS 的时间复杂度取决于所遍历路径数。而路径数是由搜索方向决定的。显然算法在最好的
情况下,即 m=n 并且 b 中所有的值都指示沿着对角线方向搜索,时间复杂度为 O(n). 相反,当 X 和 Y
序列不存在公共子序列时为算法的最坏情况,此时 C 中所有值都等于 0,数组 b 中所有的值都指示要
分别沿两个不同的方向(向左或向上)搜索,这种情况下每处理一次 X[i],Y[j]时总是要沿两个方向分
别调用 Find_All_LCS,遇到 i=0 或 j=0 时返回,直到搜索完所有的可能路径才结束,最坏情况下的搜
索矩阵如下图所示: