LeetCode 1458题要求我们找到两个数组nums1和nums2的非空子序列,使得它们的点积最大化。子序列的定义是保持相对顺序但不要求连续的元素序列。这道题的核心在于如何高效地遍历所有可能的子序列组合并计算最大点积。
给定两个数组nums1和nums2,我们需要找到:
示例:
nums1 = [2,1,-2,5], nums2 = [3,0,-6]
最大点积来自子序列[2,-2]和[3,-6]:
23 + (-2)(-6) = 6 + 12 = 18
最直观的方法是枚举所有可能的子序列组合:
这种暴力解法的时间复杂度是O(2^(m+n)),对于m=n=20时就已经超过10^12次操作,显然不可行。
观察到这个问题具有两个关键特性:
这提示我们可以使用动态规划来解决。具体来说,我们可以定义dp[i][j]表示考虑nums1前i个元素和nums2前j个元素时能获得的最大点积。
定义dp[i][j]为考虑nums1[0..i-1]和nums2[0..j-1]时的最大点积。状态转移需要考虑四种情况:
因此转移方程为:
dp[i][j] = max(
dp[i-1][j-1],
nums1[i-1]*nums2[j-1],
dp[i][j-1],
dp[i-1][j],
dp[i-1][j-1] + nums1[i-1]*nums2[j-1] # 关键:考虑延续之前的子序列
)
需要初始化dp[0][j]和dp[i][0]为负无穷,因为空子序列不符合题目要求。但dp[0][0]可以设为0作为起点。
以nums1 = [2,1,-2,5], nums2 = [3,0,-6]为例:
由于dp[i][j]只依赖于左边、上边和左上方的值,可以将空间复杂度从O(mn)优化到O(min(m,n))。只需维护前一行的dp值即可。
python复制def maxDotProduct(nums1, nums2):
m, n = len(nums1), len(nums2)
dp = [[-float('inf')] * (n + 1) for _ in range(m + 1)]
for i in range(1, m + 1):
for j in range(1, n + 1):
curr = nums1[i-1] * nums2[j-1]
dp[i][j] = max(
dp[i-1][j-1] + curr,
curr,
dp[i-1][j],
dp[i][j-1],
dp[i-1][j-1]
)
return dp[m][n]
时间复杂度:O(mn),需要填充m×n的dp表格
空间复杂度:O(mn),可以优化到O(min(m,n))
需要特别处理的情况:
当遇到错误时,可以:
建议测试这些情况:
这种类型的动态规划可以应用于:
提示:在面试中遇到这类问题时,建议先从小例子出发,明确状态定义,再推导转移方程,最后考虑优化。不要一开始就尝试写代码。