最长公共子串,动态规划,时间O(mn),空间O(min(m,n)*k), k为最长公共子序列长度(超时)
分析过程类似于:https://www.nowcoder.com/discuss/388075528203378688
公共子串条件是:同时含有并且连续([s1[i-1], s1[i]]与[s2[j-1], s1[j]]连续),存在很多局部情况,所以原问题的解不一定包含所有子问题的解,直接以最长公共子串为状态不好互相转换。以包含(i,j)点值的局部公共子串为状态,再维护一个最大最长公共子串。遍历m*n空间搜索满足条件的点,如果s1[i]=s2[j],则同时含有的条件满足,现在只需要找到与之连续的最近已知点(i-1, j-1),并把(i,j)点值加入(i-1, j-1)点状态(公共子串)中,得到(i,j)点状态,即dp[i][j]=dp[i-1][j-1] + s1[i]。如果s1[i]!=s2[j],即同时含有不满足,它不含于任何公共子串,为'',即dp[i][j]=''
由于每一行的状态只与上一行有关,可以优化dp为一维向量,但此时需要一个变量来记录(i-1, j-1)状态。
class Solution: def LCS(self , str1: str, str2: str) -> str: if len(str1) > len(str2): str1, str2 = str2, str1 # str1为小串 m, n = len(str1), len(str2) dp = ['']*(m+1) # dp[0]是不更新的占位状态,便于计算边缘出的状态值 max_str = '' # 维护一个最大公共子串 for i in range(n): pre_dp = dp[0] for j in range(m): # 根据短串更新dp if str1[j] == str2[i]: tmp = dp[j+1] # 记录下一个点的(i-1,j-1)状态 dp[j+1] = pre_dp + str1[j] # 状态转移 pre_dp = tmp else: pre_dp = dp[j+1] # 记录下一个点的(i-1,j-1)状态 dp[j+1] = '' # 清空状态 if len(max_str) < len(dp[j+1]): max_str = dp[j+1] return max_str