最长公共子序列问题Python实现
2017-10-14 19:36
260 查看
最长公共子序列问题
问题介绍
最长公共子序列(LCS)是一个在一个序列集合中(通常为两个序列)用来查找所有序列中最长子序列的问题。一个数列 ,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则称为已知序列的最长公共子序列 —— [ 百度百科 ]本文使用动态规划技术算法时间复杂度为Θ(mn),优于蛮力搜索的时间复杂度为Θ(m2n)。
使用动态规划技术,首先要寻找一个递推公式,令序列A=a1a2…an和序列B=b1b2…bn。令L[i,j]表示为a1a2…ai和b1b2…bj的最长公共子序列的长度。因为i和j可能为0,即a1a2…ai和b1b2…bj其中一个或同时为空的序列。
得到以下结论:
如果i=0或j=0,那么L[i,j]=0;
如果i和j都大于0,那么
如果ai=bi,L[i,j]=L[i-1,j-1]+1;
如果ai≠bi,L[i,j]=max(L[i,j-1],L[i-1,j])。
递推式:
L(i,j)=⎧⎩⎨0,L[i−1,j−1]+1,max(L[i,j−1],L[i−1,j]若i=0 或 j=0若i>0, j>0和ai=bj若i>0, j>0和ai≠ bj伪代码
for i ← 0 to m for j ← 0 to n L[i,j] ← 0 end for for i ← 1 to m for j ← 1 to n if a[i] = b[j] then L[i,j] ← L[i-1,j-1]+1 else then L[i,j] ← max{L[i,j-1],L[i-1,j]} end if end for end for return L[m,n]
使用Python实现,并通过标记列表以自底向下的方式计算最长子序列的最优解(两个序列在递推时最先达的最长公共子序列)
# coding = utf-8 class LCS: """ 两个序列求其最长子序列 """ def __init__(self, str1, str2): self.str1 = str1 self.str2 = str2 self.str_lcs = '' def get_flag(self): """ 通过动态规划的方式生成结果矩阵和标记矩阵 :return: 计算表和标记表 """ str1_len = self.str1.__len__() # 获取字符串的长度 str2_len = self.str2.__len__() chart = [[0 for col in range(str2_len + 1)] for row in range(str1_len + 1)] # 初始化二维列表,并将其值全部置为0 chart_flag = [['--' for col in range(str2_len + 1)] for row in range(str1_len + 1)] # 初始化二维列表,并将其值全部置为-- for col in range(1, chart_flag[0].__len__()): # 初始化标记列表的第一行和第一列为字符串a,b中字符 chart_flag[0][col] = ' ' + self.str2[col - 1] for row in range(1, chart_flag.__len__()): chart_flag[row][0] = ' ' + self.str1[row - 1] for row in range(str1_len): for col in range(str2_len): """ 如果a[row-1]=b[col-1],则a[:row]和b[:col]的最长子序列长度为a[:row-1]和b[:col-1]的最长子序列长度加一 如果a[row-1]!=b[col-1],则a[:row]和b[:col]的最长子序列长度为{{a[:row-1],b[:col]},{a[:row],b[:col-1]|最长子序列长度的最大值} """ if self.str1[row] == self.str2[col]: chart[row + 1][col + 1] = chart[row][col] + 1 chart_flag[row + 1][col + 1] = '↖' else: chart[row + 1][col + 1] = max(chart[row + 1][col], chart[row][col + 1]) if chart[row + 1][col + 1] == chart[row][col + 1]: chart_flag[row + 1][col + 1] = '↑' else: chart_flag[row + 1][col + 1] = '←' return chart, chart_flag def get_cls(self, flag, row, col): """ 利用标记矩阵自底向上来求最优解 :param flag: 标记列表 :param row: 第一个子序列的长度 :param col: 第二个子序列的长度 :return: """ if 0 == row or 0 == col: return if flag[row][col] == '↖': self.get_cls(flag, row - 1, col - 1) self.str_lcs = self.str_lcs + self.str1[row - 1] elif flag[row][col] == '←': self.get_cls(flag, row, col - 1) else: self.get_cls(flag, row - 1, col) def __str__(self): """ 重载__str__方法,相当于Java中的对象toString()方法 :return: """ print("序列({})和序列({})的最长子序列为:({})".format(self.str1, self.str2, self.str_lcs)) if __name__ == "__main__": a = "xyxxzxyzxy" b = "zxzyyzxxyxxz" my_lcs = LCS(a, b) # 构造一个具有两个字符串的对象 lcs_chart, lcs_flag = my_lcs.get_flag() # 获得计算列表和标记列表 # 显示结果矩阵和标记矩阵 print('结果矩阵如下:') for i in lcs_chart: print(i) print("标记矩阵如下:") for j in lcs_flag: print(j) # 求两个序列的最长子序列最优解并显示 my_lcs.get_cls(lcs_flag, a.__len__(), b.__len__()) my_lcs.__str__()
运行结果
利用标记矩阵自底向上求最长公共子序列,标记”↖“表示ai=bi此处的值为左上角的值+1,标记”←“表示ai≠bi(左边的值大于上边的值)取值为左边的值,标记”↑“表示ai≠bi(上边的值大于或等于左边的值)取值为上边的值。
最终序列(xyxxzxyzxy)和序列(zxzyyzxxyxxz)的最长公共子序列的最优解为(xyxxxz)。
[b]其他问题[/b]
输出最长公共子序列
关于两个序列的所有最长公共子序列的解,可以通过递归的方式实现。同样为利用标记矩阵自底向上求最长公共子序列,标记”↖“表示ai=bi此处的值为左上角的值+1,标记”←“表示ai≠bi(左边的值大于上边的值)取值为左边的值,标记”↑“表示ai≠bi(上边的值大于左边的值)取值为上边的值,标记”O“表示ai≠bi(上边的值等于左边的值)取值为上边或左边的值。
下图给出部分思路(只进行部分递归),即使用自底向上的方式行进,如果为”↑“则路径向上,为“←”则路径向左,为“↖”则路径向左上,为“O”表示进行分化(此条路径上和左两条都可以行进),达到矩阵的边缘即达到路径的终点,一条路径标记为”↖”形成的字符串代表一个最长公共子序列的解。
降低空间复杂度
①我们可以将标记矩阵和结果矩阵合并为一个含数值和标记的对象矩阵优化算法空间复杂度;
②目前设计的算法空间复杂度为Θ((m+1)(n+1))。可以通过降维操作,使算法空间复杂度从Θ((m+1)(n+1))降为Θ(min{m,n}),即选取子序列长度最小的作为一维列表的长度,将从左至右逐行填表的方式转换为在一列内位至上而下填表(如下图所示,实际空间为橙色区域)。
ps: 使用第二种空间优化方法将导致无法或者很难求出最优解和最长公共子序列的所有解。
欢迎大家留言交流
相关文章推荐
- python实现求解最长公共子序列LCS问题
- python实现最长公共子序列动态规划问题
- 最长递增子列、最长公共子序列 python实现
- python 实际问题(一)--- 实现LED字母打印
- 最长公共子序列问题-求所有公共子序列(java核心代码实现)
- 用Python实现约瑟夫问题
- 8皇后问题的c++与python实现对比
- python 使用 signal模块实现函数调用超时问题
- 棋盘覆盖问题python3实现
- Python实现各类数据结构和算法---动态规划之最长公共子序列
- 字符串问题python实现(二)
- 字符串问题python实现(三)
- 字符串问题python实现(四)
- python 多线程服务器验证,实现断开重连,信号量解决主线程Ctrl+C不退出问题,多线程打印错行问题
- 基数排序(Python实现出现的奇怪问题???)
- Python实现八皇后问题
- 一个无聊男人的疯狂《数据结构与算法分析-C++描述》学习笔记 用C++/lua/python/bash的四重实现(3) 最大子序列和问题
- 8皇后问题Python与C++实现
- 八皇后问题之python和scheme实现
- Python中利用列表推导式实现矩阵置换时发现的"问题"