Python求两个字符串最长公共子序列代码实例

一、问题描述

给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence)。比如字符串1:BDCABA;字符串2:ABCBDAB。则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA

二、算法求解

这是一个动态规划的题目。对于可用动态规划求解的问题,一般有两个特征:①最优子结构;②重叠子问题

①最优子结构

设X=(x1,x2,...,xn)和Y=(y1,y2,...,ym)是两个序列,将X和Y的最长公共子序列记为LCS(X,Y)

找出LCS(X,Y)就是一个最优化问题。因为,我们需要找到X和Y中最长的那个公共子序列。而要找X和Y的LCS,首先考虑X的最后一个元素和Y的最后一个元素。

⑴如果xn=ym,即X的最后一个元素与Y的最后一个元素相同,这说明该元素一定位于公共子序列中。因此,现在只需要找:LCS(Xn-1,Ym-1)

LCS(Xn-1,Ym-1)就是原问题的一个子问题。为什么叫子问题?因为它的规模比原问题小。

为什么是最优的子问题?因为我们要找的是Xn-1和Ym-1的最长公共子序列啊。最长的!换句话说就是最优的那个。

⑵如果xn!=ym,这下要麻烦一点,因为它产生了两个子问题:LCS(Xn-1,Ym)和LCS(Xn,Ym-1)

因为序列X和序列Y的最后一个元素不相等,那说明最后一个元素不可能是最长公共子序列中的元素。

LCS(Xn-1,Ym)表示:最长公共序列可以在(x1,x2,...xn-1)和(y1,y2,...,ym)中找。

LCS(Xn,Ym-1)表示:最长公共序列可以在(x1,x2,...xn)和(y1,y2,...,ym-1)中找。

求解上面两个子问题,得到的公共子序列谁最长,那谁就是LCS(X,Y)。用数学表示就是:

LCS=max{LCS(Xn-1,Ym),LCS(Xn,Ym-1)}

由于条件⑴和⑵考虑到了所有可能的情况。因此,我们成功的把原问题转化成了三个规模更小的问题。

②重叠子问题

重叠子问题是什么?就是说原问题转化成子问题后,子问题中有相同的问题。

原问题是:LCS(X,Y)。子问题有❶LCS(Xn-1,Ym-1)❷ LCS(Xn-1,Ym)❸ LCS(Xn,Ym-1)

乍一看,这三个问题是不重叠的。可本质上它们是重叠的,因为它们只重叠了一大部分。举例:

第二个子问题:LCS(Xn-1,Ym)就包含了问题❶LCS(Xn-1,Ym-1),为什么?

因为,当Xn-1和Ym的最后一个元素不相同时,我们又需要将LCS(Xn-1,Ym-1)进行分解:分解成:LCS(Xn-1,Ym-1)和LCS(Xn-2,Ym)

也就是说:在子问题的继续分解中,有些问题是重叠的。

由于像LCS这样的问题,它具有重叠子问题的性质,因此:用递归来求解就太不划算了。国为采用递归,它重复地求解了子问题,而且需要注意的是,所有子问题加起来的个数是指数级的。

那么问题来了,如果用递归求解,有指数级个子问题,故时间复杂度是指数级的。这指数级个子问题,难道用了动态规划,就变成多项式时间了??

关键是采用动态规划时,并不需要去一一计算那些重叠了的子问题。或者说:用了动态规划之后,有些子问题是通过“查表”直接得到的,而不是重新又计算一遍得到的。举个例子:比如求Fib数列。

求fib(5),分解成了两个子问题:fib(4)和fib(3),求解fib(4)和fib(3)时,又分解了一系列的小问题...

从图中可以看出:根的左右子树:fib(4)和fib(3)下,是有很多重叠的!比如,对于fib(2),它就一共出现了三次。如果用递归来求解,fib(2)就会被计算三次,而用DP(Dynamic Programming)动态规划,则fib(2)只会计算一次,其他两次则是通过“查表”直接求得。而且,更关键的是:查找求得该问题的解之后,就不需要再继续去分解该问题了。而对于递归,是不断地将问题解,直到分解为基准问题(fib(0)或者fib(1))

说了这么多,还是写下最长公共子序列的递归式才完整。

C[i,j]表示:(x1,x2,...,xi)和(y1,y2,...,yj)的最长公共子序列的长度。公式的具体解释可参考《算法导论》动态规划章节

三、LCS Python代码实现

#! /usr/bin/env python3
# -*- coding:utf-8 -*-

# Author  : mayi
# Blog   : http://www.cnblogs.com/mayi0312/
# Date   : 2019/5/16
# Name   : test03
# Software : PyCharm
# Note   : 用于实现求解两个字符串的最长公共子序列

def longestCommonSequence(str_one, str_two, case_sensitive=True):
  """
  str_one 和 str_two 的最长公共子序列
  :param str_one: 字符串1
  :param str_two: 字符串2(正确结果)
  :param case_sensitive: 比较时是否区分大小写,默认区分大小写
  :return: 最长公共子序列的长度
  """
  len_str1 = len(str_one)
  len_str2 = len(str_two)
  # 定义一个列表来保存最长公共子序列的长度,并初始化
  record = [[0 for i in range(len_str2 + 1)] for j in range(len_str1 + 1)]
  for i in range(len_str1):
    for j in range(len_str2):
      if str_one[i] == str_two[j]:
        record[i + 1][j + 1] = record[i][j] + 1
      elif record[i + 1][j] > record[i][j + 1]:
        record[i + 1][j + 1] = record[i + 1][j]
      else:
        record[i + 1][j + 1] = record[i][j + 1]

  return record[-1][-1]

if __name__ == '__main__':
  # 字符串1
  s1 = "BDCABA"
  # 字符串2
  s2 = "ABCBDAB"
  # 计算最长公共子序列的长度
  res = longestCommonSequence(s1, s2)
  # 打印结果
  print(res) # 4

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Python查找最长不包含重复字符的子字符串算法示例

    本文实例讲述了Python查找最长不包含重复字符的子字符串算法.分享给大家供大家参考,具体如下: 题目描述 请从字符串中找出一个最长的不包含重复字符的子字符串,计算该最长子字符串的长度.例如在"arabcacfr"中,最长的不包含重复字符的子字符串是"acfr",长度为4 采用字典的方法,最后输出所有最长字符的列表 算法示例: # -*- coding:utf-8 -*- #! python3 class Solution: def __init__(self):

  • 在Python中使用filter去除列表中值为假及空字符串的例子

    在 Python中,认为以下值为假: None # None值 False # False值 0 # 数值零不管它是int,float还是complex类型 '',(),[] # 任何一个空的序列 {} # 空的集合 如果一个列表中含上面值为假的元素,要去除的话,可以使用内置函数的filter默认的参数None. 可以先看下filter内置函数的帮助文档 >>> help(filter) Help on built-in function filter in module __built

  • Python实现统计给定字符串中重复模式最高子串功能示例

    本文实例讲述了Python实现统计给定字符串中重复模式最高子串功能.分享给大家供大家参考,具体如下: 给定一个字符串,如何得到其中重复模式最高的子字符串,我采用的方法是使用滑窗机制,对给定的字符串切分,窗口的大小从1增加到字符串长度减1,将所有的得到的切片统计结果,在这里不考虑单个字符的重复模式,好了,很简单看具体实现: #!usr/binenv python #encoding:utf-8 ''''' __Author__:沂水寒城 统计一个给定字符串中重复模式数量得到最高重复模式串 '''

  • Python针对给定字符串求解所有子序列是否为回文序列的方法

    本文实例讲述了Python针对给定字符串求解所有子序列是否为回文序列的方法.分享给大家供大家参考,具体如下: 问题: 给定一个字符串,得到所有的子序列,判断是否为回文序列 思路: 对字符串遍历切片即可 下面是具体实现: #!usr/bin/env python # -*- coding:utf-8 -*- ''''' __AUthor__:沂水寒城 功能:对指定字符串寻找所有回文子序列 ''' def is_huiwen(one_str_list): ''''' 输入一个字符串列表,判断是否为回

  • 在Python中实现替换字符串中的子串的示例

    假如有个任务: 给定一个字符串,通过查询字典,来替换给定字符中的变量.如果使用通常的方法: >>> "This is a %(var)s" % {"var":"dog"} 'This is a dog' >>> 其实可以使用string.Template类来实现上面的替换 >>> from string import Template >>> words = Template

  • python替换字符串中的子串图文步骤

    修改字符串本身是不可能的,因为字符串是不可变类型,只能是通过某些方法来产生它的副本.再把副本赋值给原字符串,达到类似替换的作用.这里介绍几种方法. 旧串换新串:使用str.replace(old, new, max) 1)字符串调用此函数时,将生成一个字符串的副本.副本中new将替代old. 2)old -原来的子串. 3)new-新子串,用于替换old. 4)max-最大替换个数,(可以不指定,为全部替换) 在指定max时,如果超出了old子串的个数,也是全部替换. 1.分割后筛选再连接: 分

  • python实现求两个字符串的最长公共子串方法

    如下所示: # coding:utf-8 ''' 求两个字符串的最长公共子串 思想:建立一个二维数组,保存连续位相同与否的状态 ''' def getNumofCommonSubstr(str1, str2): lstr1 = len(str1) lstr2 = len(str2) record = [[0 for i in range(lstr2+1)] for j in range(lstr1+1)] # 多一位 maxNum = 0 # 最长匹配长度 p = 0 # 匹配的起始位 for

  • Python实现针对给定字符串寻找最长非重复子串的方法

    本文实例讲述了Python实现针对给定字符串寻找最长非重复子串的方法.分享给大家供大家参考,具体如下: 问题: 给定一个字符串,寻找其中最长的重复子序列,如果字符串是单个字符组成的话如"aaaaaaaaaaaaa"那么满足要求的输出就是a 思路: 这里的思路有两种是我能想到的 (1)从头开始遍历字符串,设置标志位,在往后走的过程中当发现和之前标志位重合的时候就回头检查一下这个新出现的子串是否跟前面字符串或者前面字符串的子串相同,相同则记录该子串并计数加1,直至处理完毕 (2)利用滑窗切

  • Python求两个字符串最长公共子序列代码实例

    一.问题描述 给定两个字符串,求解这两个字符串的最长公共子序列(Longest Common Sequence).比如字符串1:BDCABA:字符串2:ABCBDAB.则这两个字符串的最长公共子序列长度为4,最长公共子序列是:BCBA 二.算法求解 这是一个动态规划的题目.对于可用动态规划求解的问题,一般有两个特征:①最优子结构:②重叠子问题 ①最优子结构 设X=(x1,x2,...,xn)和Y=(y1,y2,...,ym)是两个序列,将X和Y的最长公共子序列记为LCS(X,Y) 找出LCS(X

  • java实现求两个字符串最长公共子串的方法

    本文实例讲述了java实现求两个字符串最长公共子串的方法.分享给大家供大家参考,具体如下: 这个是华为OJ上的一道题目.首先,如果我们用java写代码,华为OJ有以下三条规则需遵守,否则编译无法通过或者用例无法通过,规则如下: (1)一定不可以有包名: (2)主类名只能为Main: (3)不可以输出与结果无关的信息. 好了,按照以上规则,我们写出来的代码如下(此代码不是最优的,只是用来记录华为OJ上java代码的书写规则): import java.util.Scanner; public cl

  • PHP实现求两个字符串最长公共子串的方法示例

    本文实例讲述了PHP实现求两个字符串最长公共子串的方法.分享给大家供大家参考,具体如下: 前面一篇PHP实现求解最长公共子串问题的方法是基于java改进而来,这里再来看另一种公共子串算法. 代码如下: <?php $a = 'abceee12345309878'; $b = 'abceeew2345i09878fsfsfsfabceeewsfsdfsfsabceeew'; $c = array(); $lenht1 = strlen($a); $lenth2 = strlen($b); $sta

  • java实现字符串匹配求两个字符串的最大公共子串

    本文实例讲述了java实现求两个字符串最大公共子串的方法.分享给大家供大家参考,具体如下: 最近在项目工作中有一个关于文本对比的需求,经过这段时间的学习,总结了这篇博客内容:求两个字符串的最大公共子串. 算法思想:基于图计算两字符串的公共子串.具体算法思想参照下图: 输入字符串S1:achmacmh    输入字符串S2:macham 第a步,是将字符串s1,s2分别按字节拆分,构成一个二维数组: 二维数组中的值如b所示,比如第一行第一列的值表示字符串s2和s1的第一个字节是否相等,若相等就是1

  • JavaScript自定义函数实现查找两个字符串最长公共子串的方法

    本文实例讲述了JavaScript自定义函数实现查找两个字符串最长公共子串的方法.分享给大家供大家参考,具体如下: //查找两个字符串的最长公共子串 function findSubStr(s1,s2){ var S=sstr= "" ,L1=s1.length,L2=s2.length; if (L1>L2){ var s3=s1;s1=s2,s2=s3,L1=s2.length;} for ( var j=L1;j> 0 ;j--) for ( var i= 0 ;i&

  • 详解Python最长公共子串和最长公共子序列的实现

    最长公共子串(The Longest Common Substring) LCS问题就是求两个字符串最长公共子串的问题.解法就是用一个矩阵来记录两个字符串中所有位置的两个字符之间的匹配情况,若是匹配则为1,否则为0.然后求出对角线最长的1的序列,其对应的位置就是最长匹配子串的位置. def find_lcsubstr(s1, s2): m=[[0 for i in range(len(s2)+1)] for j in range(len(s1)+1)] #生成0矩阵,为方便后续计算,比字符串长度

  • C++动态规划实现查找最长公共子序列

    目录 最长公共子序列 代码实现 结果 最长公共子序列 最长公共子序列(LCS)是一个在一个序列集合中(通常为两个序列)用来查找所有序列中最长子序列的问题.一个数列 ,如果分别是两个或多个已知数列的子序列,且是所有符合此条件序列中最长的,则称为已知序列的最长公共子序列. 动态规划: 采用二维数组flag来记录下标i和j的走向.数字"1"表示,斜向下:数字"2"表示,水平向右:数字"3"表示,竖直向下 问题描述: 设有字符串a[0…n],b[0…m]

  • C语言求两个字符串的最长公共子串

    本文实例讲述了C语言求两个字符串的最长公共子串的方法.分享给大家供大家参考.具体实现方法如下: #include "stdio.h" #include "string.h" #include "stdlib.h" void getCommon(char str1[],char str2[],char * str3); int stringLength(char * str); void main(){ char str1[50]; char st

  • Java基于动态规划法实现求最长公共子序列及最长公共子字符串示例

    本文实例讲述了Java基于动态规划法实现求最长公共子序列及最长公共子字符串.分享给大家供大家参考,具体如下: 动态规划法 经常会遇到复杂问题不能简单地分解成几个子问题,而会分解出一系列的子问题.简单地采用把大问题分解成子问题,并综合子问题的解导出大问题的解的方法,问题求解耗时会按问题规模呈幂级数增加. 为了节约重复求相同子问题的时间,引入一个数组,不管它们是否对最终解有用,把所有子问题的解存于该数组中,这就是动态规划法所采用的基本方法. [问题] 求两字符序列的最长公共字符子序列 问题描述:字符

随机推荐