【题目】
给定两个字符串str1和str2,返回两个字符串的最长公共子串。
【举例】
str1 = “1AB2345CD”,str2 = “12345EF”,返回”2345”。
【基本思路】
假设str1的长度为N,str2的长度为M,生成N×M的矩阵dp,dp[i][j]的含义是必须以str1[i]和str2[j]结尾的最长公共子串的长度,dp[i][j]的计算方法如下:
1.矩阵的第一行。如果str1[0] == str2[j],此时以str1[0]和str2[j]结尾的最长公共子串就是它们自身,dp[0][j]设为1。
2.矩阵的第一列。如果str1[i] == str2[0],dp[i][0]设为1。
3. 矩阵的其它位置计算如下:
1)如果str1[i] != str2[j],说明以str1[i]和str2[j]结尾的子串是不存在的,设为0。
2) 如果str1[i] == str2[j],说明str1[i]和str2[j]可以作为公共子串的最后一个字符,从最后一个字符向左能扩多大的长度呢?就是dp[i-1][j-1]的值,所以dp[i][j] = dp[i-1][j-1] + 1。
生成dp表之后,再得到最长的公共子串就很容易了。
dp表中的最大值(假设为dp[x][y])就是最长公共子串的长度(假设为maxlen),str1中从下标x开始向左数的maxlen个字符就是最长的公共子串。
因为dp[i][j]的值只依赖于dp[i-1][j-1],所以可以用空间压缩的方法将空间复杂度从O(N*M)降低到O(1)。
下面是用python3.5实现的代码
#最长公共子串问题
#经典动态规划方法。时间复杂度O(M*N),空间复杂度O(M*N)
def maxCommonSubStr(str1, str2):
def getdp(str1, str2):
dp = [[0 for i in range(len(str2))] for j in range(len(str1))]
for i in range(len(str2)):
if str2[i] == str1[0]:
dp[0][i] = 1
for i in range(len(str1)):
if str1[i] == str2[0]:
dp[i][0] = 1
for i in range(1, len(str1)):
for j in range(1, len(str2)):
if str1[i] == str2[j]:
dp[i][j] = dp[i-1][j-1] + 1
return dp
if str1 == None or str2 == None or str1 == '' or str2 == '':
return ""
dp = getdp(str1, str2)
length = 0
index = 0
for i in range(len(str1)):
for j in range(len(str2)):
if dp[i][j] > length:
length = dp[i][j]
index = i
return str1[index-length+1 : index+1]
#动态规划升级版。时间复杂度O(M*N),空间复杂度O(1)
def maxCommonSubStr2(str1, str2):
if str1 == None or str2 == None or str1 == "" or str2 == "":
return ""
maxlen = 0
index = 0
row = 0
col = len(str2) - 1
while row < len(str1):
i = row
j = col
length = 0
while i < len(str1) and j < len(str2):
if str1[i] == str2[j]:
length += 1
else:
length = 0
if length > maxlen:
maxlen = length
index = i
i += 1
j += 1
if col > 0:
col -= 1
else:
row += 1
return str1[index-maxlen+1 : index+1]