题目描述 给定两个字符串str1和str2,输出两个字符串的最长公共子串,如果最长公共子串为空,输出-1。 示例1 输入 "1AB2345CD","12345EF" 返回值 "2345" niuke
给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。 一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。 例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。 若这两个字符串没有公共子序列,则返回 0。 示例 1: 输入:text1 = "abcde", text2 = "ace" 输出:3
解释:最长公共子序列是 "ace",它的长度为 3。 链接:
最长公共子串问题是典型的动态规划问题,是典型的动态规划问题:因为它要求的是最长公共子串,是在求子序列的问题,而动态规划就是为解决这样一类问题而生的,先复习一下动态规划的概念及基础知识:
动态规划(Dynamic programming)是一种在数学、计算机科学和经济学中使用的,通过把原问题分解为相对简单的子问题的方式求解复杂问题的方法。 动态规划常常适用于有重叠子问题和最优子结构性质的问题,动态规划方法所耗时间往往远少于朴素解法。
动态规划背后的基本思想非常简单。大致上,若要解一个给定问题,我们需要解其不同部分(即子问题),再合并子问题的解以得出原问题的解。 通常许多子问题非常相似,为此动态规划法试图仅仅解决每个子问题一次,从而减少计算量: 一旦某个给定子问题的解已经算出,则将其记忆化存储,以便下次需要同一个子问题解之时直接查表。 这种做法在重复子问题的数目关于输入的规模呈指数增长时特别有用。 关于动态规划最经典的问题当属背包问题。背包问题详解
即:
【初始状态】→【决策1】→【决策2】→…→【决策n】→【结束状态】
注意:
str1=a1,a2,a3..
的长度为m,str2=b1,b2,b3...
的长度为n,则dp[i][j]表示为a1a2…ai和b1b2…bj的最长公共子序列的长度。因为i和j可能为0,即a_1,a_2,...,a_i和b1,b_2,..., b_j其中一个或同时为空的序列。 故此,有:最长公共子串:表示的是两个单词中连续的相同字母长度; 最长公共子序列:表示的是两个单词中相同的字母的长度。 他们的区别在于字符是否连续
from matplotlib import pyplot as plt
import numpy as np
"""
最长公共子串:表示的是两个单词中连续的相同字母长度
"""
class Solution:
#实现方法1
def LCS(self , str1 , str2 ):
# write code here
if len(str1) > len(str2):
str1, str2 = str2, str1
max_len, res = 0, ''
for i in range(len(str1)):
print("i:t",i)
print("i-max_len~i:",i-max_len,"~",i+1,"tstr1:t",str1[i-max_len:i+1])
if str1[i-max_len: i + 1] in str2:
res = str1[i-max_len: i + 1]
max_len += 1
print("res:t",res)
print("max_len:t",max_len)
if not res:
return '-1'
else:
return res
# 实现方法2
def getLcsChar(self,str1,str2):
if len(str1)>len(str2):
str1,str2= str2,str1
str1_len = len(str1)
str2_len = len(str2)
dp=[[0 for col in range(str2_len+1)] for row in range(str1_len+1) ]
p = 0 #最大匹配长度对应在str1中的最后
max_len = 0 #最大匹配长度
for row in range(str1_len):
for col in range(str2_len):
if str1[row]==str2[col]:
dp[row+1][col+1]=dp[row][col]+1
if dp[row+1][col+1]>max_len:
max_len=dp[row+1][col+1]
p = row+1
return str1[p-max_len:p], max_len
# 最长公共子序列的长度
def recursive_lcs(self,str_a, str_b):
if len(str_a)>len(str_b):
str_a,str_b=str_b,str_a
ret_c=""
if len(str_a) == 0 or len(str_b) == 0:
return 0
if str_a[0] == str_b[0]:
return self.recursive_lcs(str_a[1:], str_b[1:]) + 1
else:
return max([self.recursive_lcs(str_a[1:], str_b), self.recursive_lcs(str_a, str_b[1:])])
S=Solution()
str1="1AB2345CD"
str2="12345EF"
print("LCS:",S.LCS(str1,str2))
print("recursive_lcs",S.recursive_lcs(str1,str2))
str_ret, num=S.getLcsChar(str1,str2)
print("getLcsChar:",str_ret, num)
"""
最长公共子序列:表示的是两个单词中相同的字母的长度。
比如:fosh,与fish相近还是与fort相近呢?
如果用最长公共子串的方法则是相同的,都有两个字母连续相同。但是,
这里我们是不是应该一眼看出其实fish更相近一些呢,因为它有三个字母与fosh相同。
"""
class LCS:
"""
求str1,str2最长公共子序列
"""
def __init__(self,str1,str2):
self.str1=str1
self.str2=str2
self.lcs_ret=''
def getFlag(self):
"""
通过动态规划算法生成标记矩阵和结果矩阵
"""
# 获取字符串长度
str1_len=self.str1.__len__()
str2_len=self.str2.__len__()
# if str1_len>str2_len:
# self.str1, self.str2 = self.str2, self.str1
# str1_len=self.str1.__len__()
# str2_len=self.str2.__len__()
# 初始化结果矩阵和flag矩阵
ret_list=[[0 for col in range(str2_len+1)] for row in range(str1_len+1)]
flag_list=[["$" for col in range(str2_len+1)] for row in range(str1_len+1)]
# # 设flag_list中的第一行和第一列为str1,str2中的字符
for col in range(0,flag_list[0].__len__()-1):
#print(self.str2[col])
flag_list[0][col]=""+self.str2[col]
for row in range(1,flag_list.__len__()):
flag_list[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]的最长子序列长度加一
if self.str1[row]==self.str2[col]:
ret_list[row+1][col+1]=ret_list[row][col]+1
flag_list[row+1][col+1]="ok"
# 否则:a[row-1]!=b[col-1],则a[:row]和b[:col]的最长子序列长度为{
{a[:row-1],b[:col]},{a[:row],b[:col-1]|最长子序列长度的最大值}
else:
# ret_list[row+1][col+1]=max(ret_list[row+1][col],ret_list[row][col+1])
# if ret_list[row+1][col+1]==ret_list[row][col+1]:
# flag_list[row+1][col+1]="up"
# else:
# flag_list[row+1][row+1]="left"
if ret_list[row][col+1]>=ret_list[row+1][col]:
ret_list[row+1][col+1]=ret_list[row][col+1]
flag_list[row+1][col+1]="up"
elif ret_list[row][col+1]
i: 0
i-max_len~i: 0 ~ 1 str1: 1
res: 1
max_len: 1
i: 1
i-max_len~i: 0 ~ 2 str1: 12
i: 2
i-max_len~i: 1 ~ 3 str1: 23
res: 23
max_len: 2
i: 3
i-max_len~i: 1 ~ 4 str1: 234
res: 234
max_len: 3
i: 4
i-max_len~i: 1 ~ 5 str1: 2345
res: 2345
max_len: 4
i: 5
i-max_len~i: 1 ~ 6 str1: 2345E
i: 6
i-max_len~i: 2 ~ 7 str1: 345EF
LCS: 2345
recursive_lcs 5
getLcsChar: 2345 4
结果矩阵如下:
[0, 0, 0, 0, 0, 0, 0, 0]
[0, 0, 1, 1, 1, 1, 1, 1]
[0, 0, 1, 1, 1, 2, 2, 2]
[0, 0, 1, 2, 2, 2, 2, 2]
[0, 1, 1, 2, 2, 2, 3, 3]
[0, 1, 2, 2, 3, 3, 3, 4]
[0, 1, 2, 2, 3, 3, 4, 4]
标记矩阵如下:
['A', 'B', 'C', 'B', 'D', 'A', 'B', '$']
['B', 'up', 'ok', 'left', 'ok', 'left', 'left', 'ok']
['D', 'up', 'up', 'up', 'up', 'ok', 'left', 'left']
['C', 'up', 'up', 'ok', 'left', 'up', 'up', 'up']
['A', 'ok', 'up', 'up', 'up', 'up', 'ok', 'left']
['B', 'up', 'ok', 'up', 'ok', 'left', 'up', 'ok']
['A', 'ok', 'up', 'up', 'up', 'up', 'ok', 'up']
序列(BDCABA)和序列(ABCBDAB)的最长子序列为:(BDAB)
最长公共子序列的长度: 4