📅  最后修改于: 2023-12-03 15:10:37.396000             🧑  作者: Mango
最长公共子串(Longest Common Substring,简称 LCS)指的是两个字符串中,最长的有相同字串(即连续子串)的长度。
例如,字符串 A = "abcdefg",字符串 B = "defghij",它们的最长公共子串为 "def",长度为 3。
我们可以使用动态规划(DP)来解决最长公共子串问题。但是,朴素的 DP 解法的空间复杂度较高,为 O(mn),其中 m 和 n 分别为两个字符串的长度。
在本文中,将介绍一种空间优化 DP 方案,其空间复杂度可降至 O(n)。
空间优化 DP 方案的核心思想是:在 DP 过程中,只需要记录当前计算所需的状态,而不需要记录所有的状态。
在 最长公共子序列 的空间优化 DP 方案中,我们只需记录上一个状态和当前状态,就能在 O(1) 空间内完成计算。但对于 最长公共子串,我们需要记录当前状态之前的状态,因为它们在计算当前状态时都会被用到。
我们可以借助滚动数组的思想,将二维数组优化为一维数组,并以一定的顺序依次计算出所有状态。具体来说,我们可以按行或按列的顺序计算状态,并将计算出的状态保存在一维数组中。这样,我们就只需要记录两个一维数组,即当前状态和前一个状态,就能完成整个 DP 过程,从而将空间复杂度降至 O(n)。
以下是最长公共子串的空间优化 DP 代码片段:
def longest_common_substring(s1: str, s2: str) -> int:
m, n = len(s1), len(s2)
dp = [0] * n # 当前状态
pre_dp = [0] * n # 前一个状态
max_len = 0
for i in range(m):
for j in range(n):
if s1[i] == s2[j]:
if i == 0 or j == 0:
dp[j] = 1 # 处理边界
else:
dp[j] = pre_dp[j - 1] + 1 # 转移方程
max_len = max(max_len, dp[j])
else:
dp[j] = 0 # 不匹配则置为0
pre_dp[j] = dp[j] # 更新前一个状态
return max_len
在上述代码中,我们首先定义了两个一维数组:dp
和 pre_dp
,分别代表当前状态和前一个状态。然后,我们按行的顺序依次计算状态,用 dp[j]
表示当前状态下以 s1[i] 和 s2[j] 结尾的最长公共子串的长度,用 pre_dp[j]
表示前一个状态下以 s1[i-1] 和 s2[j-1] 结尾的最长公共子串的长度。
对于每个状态 dp[j]
,如果 s1[i] 和 s2[j] 相等,则可以根据转移方程 dp[j] = pre_dp[j-1] + 1
计算出 dp[j]
;否则, dp[j]
应该为 0。每次计算完当前状态后,我们将 dp
数组保存为 pre_dp
数组,以备下一次计算使用。最终,我们遍历所有状态,找到最长的公共子串长度,并将其返回。
空间优化 DP 方案通过记录当前状态和前一个状态,将二维数组优化为一维数组,并以一定的顺序依次计算出所有状态,从而将空间复杂度降至 O(n)。本文介绍了最长公共子串的空间优化 DP 方案,给出了对应的代码片段,并对其进行了解释。通过这种方案,我们可以在 O(n) 的空间复杂度下解决最长公共子串问题,从而为字符串匹配等问题的解决提供了更加高效简洁的解决方案。