动态规划求解最长公共子串与公共子序列
2018-01-20 18:24
483 查看
最长公共子序列(longest common sequence)和最长公共子串(longest common substring)是两个不同的问题。一个给定的序列的子序列,就是将给定序列中零个或多个元素去掉之后得到的结果。什么是子串呢?给定串中任意个连续的字符组成的子序列称为该串的子串。
(一)最长公共子序列:求得'ABCD'与'EFGACXD'最长公共子序列,结果为3。
假设动态规划中res[i][j]的值表示list1[i]与list2[j]为同尾时,最长的公共子序列长度。如果list1[i]与list2[j]相同,则res[i][j]=res[i-1][j-1] + 1,否则max(res[i-1][j], res[i][j-1])。
为了便于首行与首列的数据生成,则res的二维数组初始化为len1+1行,len2+1列,这样i+1,j+1位置的数据表示的是list1[i]与list2[j]对齐时候的最长公共子序列长度。
![](https://img-blog.csdn.net/20180120225531139?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWFzaGlqaWE5ODY=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(二)最长公共子串:给定两个字符串,求出它们之间最长的相同子字符串的长度。
设置一个二维矩阵,行数为len(list1),列数为len(list2),每个res[i][j]表示list1的i位置与list2的j位置对齐时候,这个位置之前的最长子串长度。显然,如果list1[i] != list2[j], res[i][j] = 0, 如果list1[i+1] = list2[j+1],那么res[i+1][j+1] = res[i][j] + 1,否则res[i+1][j+1] = 0。这个二维矩阵是沿-45度方向逐步生成的。
优化思路:由于是按行推导过来的,可以只用两个list保存i行与i+1行,滚动覆盖,这样能节省一定存储空间。第0行为初始化的,当做偶数行,之后每行先判断是奇数行还是偶数行,如果是奇数行,先清空,再用偶数行的值计算;如果是偶数行,先清空,用奇数行的值计算。
运行结果如下。
(一)最长公共子序列:求得'ABCD'与'EFGACXD'最长公共子序列,结果为3。
假设动态规划中res[i][j]的值表示list1[i]与list2[j]为同尾时,最长的公共子序列长度。如果list1[i]与list2[j]相同,则res[i][j]=res[i-1][j-1] + 1,否则max(res[i-1][j], res[i][j-1])。
为了便于首行与首列的数据生成,则res的二维数组初始化为len1+1行,len2+1列,这样i+1,j+1位置的数据表示的是list1[i]与list2[j]对齐时候的最长公共子序列长度。
def longest_common_subsequence(list1, list2): len1 = len(list1) len2 = len(list2) if len1 <=0 or len2 <= 0: return 0 res = [[0 for i in range(len2+1)] for i in range(len1+1)] for i in range(len1): for j in range(len2): if list1[i] == list2[j]: res[i+1][j+1] = res[i][j] + 1 else: res[i+1][j+1] = max(res[i][j+1], res[i+1][j]) return res[-1][-1] list1 = 'ABCD' list2 = 'EFGACXD' print(longest_common_subsequence(list(list1), list(list2)))
(二)最长公共子串:给定两个字符串,求出它们之间最长的相同子字符串的长度。
设置一个二维矩阵,行数为len(list1),列数为len(list2),每个res[i][j]表示list1的i位置与list2的j位置对齐时候,这个位置之前的最长子串长度。显然,如果list1[i] != list2[j], res[i][j] = 0, 如果list1[i+1] = list2[j+1],那么res[i+1][j+1] = res[i][j] + 1,否则res[i+1][j+1] = 0。这个二维矩阵是沿-45度方向逐步生成的。
优化思路:由于是按行推导过来的,可以只用两个list保存i行与i+1行,滚动覆盖,这样能节省一定存储空间。第0行为初始化的,当做偶数行,之后每行先判断是奇数行还是偶数行,如果是奇数行,先清空,再用偶数行的值计算;如果是偶数行,先清空,用奇数行的值计算。
def longest_common_substring(list1, list2): if len(list1) == 0 or len(list2) == 0: return (0, 0, 0)#返回最大长度,begin位置,end位置 len1 = len(list1) len2 = len(list2) longest = 0 position = [] res = [[0 for i in range(len2+1)] for i in range(len1+1)] for i in range(len1): for j in range(len2): if list1[i] == list2[j]: res[i+1][j+1] = res[i][j] + 1 if res[i+1][j+1] > longest: longest = res[i+1][j+1] position = [(longest, i-longest+1, j-longest+1)] elif res[i+1][j+1] == longest: position.append((longest, i-longest+1, j-longest+1)) else: res[i+1][j+1] = 0 return position list1 = 'ABC' list2 = 'EFG' print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2)))) list1 = 'ABC' list2 = 'EFGA' print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2)))) list1 = 'ABCABC' list2 = 'ABXYYAB' print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2)))) list1 = 'ABCDEF' list2 = 'EFABCD' print('%s---%s, result:%s\n'%(list1, list2, longest_common_substring(list(list1), list(list2))))
运行结果如下。
相关文章推荐
- 动态规划 - 最长公共子序列 - 最长公共子串 - 最长不重复子串 - 最长递增子序列 - 最长回文子串
- 最大连续子序列和,乘积,最长递增子串,最长公共子串,子序列等问题(动态规划等)
- 动态规划方法求解最长公共子串
- 动态规划之最长公共子串
- 求解最大子序列、最长递增子序列、最长公共子串、最长公共子序列
- 动态规划之最长公共子串(递归的备忘录写法)
- 动态规划之最长公共子序列&最长公共子串
- 动态规划---最长的公共子序列java版本
- 动态规划-最长公共子串
- 动态规划 —— 最长公共子串(输出长度和第一个出现的子串)
- 动态规划 之 【最长公共序列】 LCS算法
- 动态规划实例(十三):最长公共子串(LCS)
- 最长公共子串(动态规划的思想)
- 动态规划:最长公共子串长度
- 动态规划:求最长公共子串/最长公共子序列
- 动态规划之最大公共序列+最长上升子序列
- 动态规划 递归 例子 string 回文串-最长公共子串
- 最长公共子串LCS--之动态规划
- 动态规划之最长公共子序列和最长公共字串,最大子序列和
- 动态规划--最长公共子串