两个子序列的最大点积
难度:
标签:
题目描述
给你两个数组 nums1
和 nums2
。
请你返回 nums1
和 nums2
中两个长度相同的 非空 子序列的最大点积。
数组的非空子序列是通过删除原数组中某些元素(可能一个也不删除)后剩余数字组成的序列,但不能改变数字间相对顺序。比方说,[2,3,5]
是 [1,2,3,4,5]
的一个子序列而 [1,5,3]
不是。
示例 1:
输入:nums1 = [2,1,-2,5], nums2 = [3,0,-6] 输出:18 解释:从 nums1 中得到子序列 [2,-2] ,从 nums2 中得到子序列 [3,-6] 。 它们的点积为 (2*3 + (-2)*(-6)) = 18 。
示例 2:
输入:nums1 = [3,-2], nums2 = [2,-6,7] 输出:21 解释:从 nums1 中得到子序列 [3] ,从 nums2 中得到子序列 [7] 。 它们的点积为 (3*7) = 21 。
示例 3:
输入:nums1 = [-1,-1], nums2 = [1,1] 输出:-1 解释:从 nums1 中得到子序列 [-1] ,从 nums2 中得到子序列 [1] 。 它们的点积为 -1 。
提示:
1 <= nums1.length, nums2.length <= 500
-1000 <= nums1[i], nums2[i] <= 100
点积:
定义a = [a1, a2,…, an]
和b
= [b1, b2,…, bn]
的点积为:这里的 Σ 指示总和符号。
代码结果
运行时间: 154 ms, 内存: 16.2 MB
/*
* 思路:
* 使用动态规划结合Java Stream API实现。
* 创建一个二维数组 dp,其中 dp[i][j] 表示 nums1 到第 i 个元素和 nums2 到第 j 个元素的最大点积。
* 递推公式是:
* dp[i][j] = max(dp[i-1][j-1] + nums1[i-1]*nums2[j-1], dp[i-1][j], dp[i][j-1], nums1[i-1]*nums2[j-1])
* 最终答案是 dp[m][n]。
*/
import java.util.stream.IntStream;
public int maxDotProduct(int[] nums1, int[] nums2) {
int m = nums1.length;
int n = nums2.length;
int[][] dp = new int[m + 1][n + 1];
IntStream.rangeClosed(0, m).forEach(i -> IntStream.rangeClosed(0, n).forEach(j -> dp[i][j] = Integer.MIN_VALUE));
IntStream.range(1, m + 1).forEach(i -> {
IntStream.range(1, n + 1).forEach(j -> {
dp[i][j] = Math.max(dp[i][j], dp[i-1][j-1] + nums1[i-1] * nums2[j-1]);
dp[i][j] = Math.max(dp[i][j], dp[i-1][j]);
dp[i][j] = Math.max(dp[i][j], dp[i][j-1]);
dp[i][j] = Math.max(dp[i][j], nums1[i-1] * nums2[j-1]);
});
});
return dp[m][n];
}
解释
方法:
题解使用动态规划的方法来求解两个数组的子序列的最大点积。定义 dp[i][j] 表示 nums1 中前 i 个元素和 nums2 中前 j 个元素的最大点积。为了优化空间复杂度,使用一维数组 f[j] 来表示当前行的状态,pre 用来存储上一行的状态。遍历 nums1 和 nums2 的每个元素,计算当前元素对最大点积的贡献,并更新状态。如果数组中全部元素为负数,还考虑了直接取最小值乘以最大值的情况。
时间复杂度:
O(m*n)
空间复杂度:
O(n)
代码细节讲解
🦆
在题解中提到,如果数组中全部元素为负数,会考虑直接取最小值乘以最大值的情况。这种方法是否总是有效?在什么情况下这种方法不适用?
▷🦆
在动态规划的迭代过程中,使用了 `max(f[j+1], f[j], pre + x1 * x2)` 来更新状态。能否解释这一更新策略背后的逻辑和其如何确保得到最大点积?
▷🦆
题解中未明确说明当 nums1 或 nums2 为空数组时该如何处理。这种边界情况应该如何处理以确保程序的鲁棒性?
▷