最长公共子序列问题Python实现
来源:互联网 发布:淘宝店铺主题风格手机 编辑:程序博客网 时间:2024/05/19 04:28
最长公共子序列问题
问题介绍
最长公共子序列(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] ← 0end forfor 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 forend forreturn L[m,n]
使用Python实现,并通过标记列表以自底向下的方式计算最长子序列的最优解(两个序列在递推时最先达的最长公共子序列)
# coding = utf-8class 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)。
其他问题
输出最长公共子序列
关于两个序列的所有最长公共子序列的解,可以通过递归的方式实现。同样为利用标记矩阵自底向上求最长公共子序列,标记”↖“表示ai=bi此处的值为左上角的值+1,标记”←“表示ai≠bi(左边的值大于上边的值)取值为左边的值,标记”↑“表示ai≠bi(上边的值大于左边的值)取值为上边的值,标记”O“表示ai≠bi(上边的值等于左边的值)取值为上边或左边的值。
下图给出部分思路(只进行部分递归),即使用自底向上的方式行进,如果为”↑“则路径向上,为“←”则路径向左,为“↖”则路径向左上,为“O”表示进行分化(此条路径上和左两条都可以行进),达到矩阵的边缘即达到路径的终点,一条路径标记为”↖”形成的字符串代表一个最长公共子序列的解。
降低空间复杂度
①我们可以将标记矩阵和结果矩阵合并为一个含数值和标记的对象矩阵优化算法空间复杂度;
②目前设计的算法空间复杂度为Θ((m+1)(n+1))。可以通过降维操作,使算法空间复杂度从Θ((m+1)(n+1))降为Θ(min{m,n}),即选取子序列长度最小的作为一维列表的长度,将从左至右逐行填表的方式转换为在一列内位至上而下填表(如下图所示,实际空间为橙色区域)。
ps: 使用第二种空间优化方法将导致无法或者很难求出最优解和最长公共子序列的所有解。
欢迎大家留言交流
- 最长公共子序列问题Python实现
- python实现求解最长公共子序列LCS问题
- 最长公共子序列(LCS)问题 python
- 最长公共子序列python实现
- 最长公共子序列及Python实现
- Python 实现最长公共子序列LCS
- 最长递增子列、最长公共子序列 python实现
- 最长公共子序列-python
- 最长公共子序列 python
- python -- 最长公共子序列
- 最长公共子序列问题
- 最长公共子序列问题
- 最长公共子序列问题
- 最长公共子序列问题
- 最长公共子序列问题
- 最长公共子序列问题
- 最长公共子序列问题
- 最长公共子序列问题
- HashMap底层实现
- 杭电acm刷题记录ID1000
- 安装mysql5.5教程
- 树莓派GPIO口上手
- TCP/IP协议与辅助协议
- 最长公共子序列问题Python实现
- java构造方法的调用顺序
- 深度| 人工智能如何帮你找到好歌:探秘Spotify神奇的每周歌单
- CCF-20170902-公共钥匙盒(30分)
- Python入门(四)——模块,__name__属性,dir()函数,包的概念
- Redis入门
- cloud项目中org.springframework.beans.factory.NoSuchBeanDefinitionException
- TMeter Premium(宽带仪表软件)官方正式版V16.0.860下载
- Collection集合框架的结构