给两个整数数组 A 和 B ,返回两个数组中公共的、长度最长的子数组的长度。
示例 1:
输入:
A: [1,2,3,2,1]
B: [3,2,1,4,7]
输出: 3
**解释: **
长度最长的公共子数组是 [3, 2, 1]。
说明:
1 <= len(A), len(B) <= 1000
0 <= A[i], B[i] < 100
很容易就可以想到开一个暴力解法,即枚举数组A中的起始位置i与数据B中的起始位置j,然后计算A[i:]与B[j:]的最长公共前缀k,最终答案即为所有的最长公共前缀的最大值。
def findLength_violence(self, A: List[int], B: List[int]) -> int:
lengthA, lengthB, ans = len(A), len(B), 0
for i in range(lengthA):
for j in range(lengthB):
k = 0
while i + k < lengthA and j + k < lengthB and A[i + k] == B[j + k]:
k += 1
ans = max(ans, k)
return ans
时间复杂度:O(N3)
空间复杂度:O(1)
暴力解法的过程中,最坏情况下对于任意i与j,A[i]与B[j]比较了min(i+1, j+1)次,这也是导致暴力解法时间复杂度过高的根本原因。
我们可以优化这一过程,使得任意一对A[i]和B[i]都只被比较一次,这样我们自然而然想到利用这一次的比较结果,如果A[i]==B[j],那么我们知道A[i:]和B[j:]的最长公共前缀为A[i+1:]与B[j+1:]的最长公共前缀+1,否则我们知道A[i:]和B[j:]的最长公共前缀为零。
这样我们就可以提出动态规划的解法:令dp[i][j]表示A[i:]和B[j:]的最长公共前缀,那么答案即为所有dp[i][j]中的最大值,如果A[i] == B[j],那么dp[i][j] = dp[i + 1][j + 1] + 1,否则dp[i][j] = 0。
考虑到这里dp[i][j]的值从dp[i + 1][j + 1]转移得到,所以我们需要倒过来,首先计算dp[len(A) - 1][len(B) - 1],最后计算dp[0][0]。
ans, lengthA, lengthB = 0, len(A), len(B)
dp = [[0] * (lengthB + 1) for _ in range(lengthA + 1)]
for i in range(lengthA - 1, -1, -1):
for j in range(lengthB - 1, -1, -1):
dp[i][j] = dp[i + 1][j + 1] + 1 if A[i] == B[j] else 0
ans = max(ans, dp[i][j])
return ans
时间复杂度: O(N×M)。
空间复杂度: O(N×M)。
N 表示数组 A 的长度,M 表示数组 B 的长度。
空间复杂度还可以再优化,利用滚动数组可以优化到 O(min(N,M))。