python 最大子序列,最长子序列算法 python

  python 最大子序列,最长子序列算法 python

  

  Python-求解两个字符串的最长公共子序列

  一、问题描述

  给定两个字符串,求解这两个字符串的最长公共序列。比如字符串1:BDC ABA;2: abcb dab。那么这两个字符串的最长公共子序列长度是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的最后一个元素

  (1)如果xn=ym,即x的最后一个元素与y的最后一个元素相同,则意味着这个元素一定在一个公共子序列中。因此,现在我们只需要找到:LCS(Xn-1,Ym-1)

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

  为什么是最优子问题?因为我们在寻找Xn-1和Ym-1的最长公共子序列。最长的!换句话说,最好的一个。

  (2)如果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=马克斯{LCS(Xn-1,Ym),LCS(Xn,Ym-1)}

  因为条件(1)和(2)考虑了所有可能的情况。因此,我们成功地将原问题转化为三个更小的问题。

  相关:《Python视频教程》

  重叠子问题

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

  原问题是: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(动态规划)动态规划,fib(2)只计算一次,另外两次直接用“查表”得到。更何况,重点是:找到这个问题的解决方案后,就不需要继续分解问题了。对于递归,问题被连续求解,直到分解成一个基准问题(fib(0)或fib(1))。

  说到这里,写下最长公共子序列的递归形式才算完整。

  C[i,j]表示(x1,x2,xi)和(y1,y2,yj)。公式的具体解释请参考《算法导论》章动态编程。

  三、LCS Py

  thon代码实现

  

#!/usr/bin/envpython3

  #-*-coding:utf-8-*-

  #Note:用于实现求解两个字符串的最长公共子序列

  deflongestCommonSequence(str_one,str_two,case_sensitive=True):

  """

  str_one和str_two的最长公共子序列

  :paramstr_one:字符串1

  :paramstr_two:字符串2(正确结果)

  :paramcase_sensitive:比较时是否区分大小写,默认区分大小写

  :return:最长公共子序列的长度

  """

  len_str1=len(str_one)

  len_str2=len(str_two)

  #定义一个列表来保存最长公共子序列的长度,并初始化

  record=[[0foriinrange(len_str2+1)]forjinrange(len_str1+1)]

  foriinrange(len_str1):

  forjinrange(len_str2):

  ifstr_one[i]==str_two[j]:

  record[i+1][j+1]=record[i][j]+1

  elifrecord[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]

  returnrecord[-1][-1]

  if__name__=='__main__':

  #字符串1

  s1="BDCABA"

  #字符串2

  s2="ABCBDAB"

  #计算最长公共子序列的长度

  res=longestCommonSequence(s1,s2)

  #打印结果

  print(res)#4

郑重声明:本文由网友发布,不代表盛行IT的观点,版权归原作者所有,仅为传播更多信息之目的,如有侵权请联系,我们将第一时间修改或删除,多谢。

留言与评论(共有 条评论)
   
验证码: