Max Dot Product of Two Subsequences
HardUpdated: Aug 2, 2025
Practice on:
Problem
Given two arrays nums1 and nums2.
Return the maximum dot product between non-empty subsequences of nums1 and nums2 with the same length.
A subsequence of a array is a new array which is formed from the original array by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, [2,3,5] is a subsequence of [1,2,3,4,5] while [1,5,3] is not).
Examples
Example 1
Input: nums1 = [2,1,-2,5], nums2 = [3,0,-6]
Output: 18
Explanation: Take subsequence [2,-2] from nums1 and subsequence [3,-6] from nums2.
Their dot product is (2*3 + (-2)*(-6)) = 18.
Example 2
Input: nums1 = [3,-2], nums2 = [2,-6,7]
Output: 21
Explanation: Take subsequence [3] from nums1 and subsequence [7] from nums2.
Their dot product is (3*7) = 21.
Example 3
Input: nums1 = [-1,-1], nums2 = [1,1]
Output: -1
Explanation: Take subsequence [-1] from nums1 and subsequence [1] from nums2.
Their dot product is -1.
Constraints
1 <= nums1.length, nums2.length <= 500-1000 <= nums1[i], nums2[i] <= 1000
Solution
Method 1 – Dynamic Programming (2D DP)
Intuition
We use a 2D DP table to track the maximum dot product for all pairs of prefixes of nums1 and nums2. At each step, we can either take both elements, skip one from nums1, or skip one from nums2. We must ensure at least one pair is chosen.
Approach
- Create a DP table
dp[i][j]for the max dot product using firstielements of nums1 and firstjelements of nums2. - For each pair
(i, j), consider:- Take both:
nums1[i] * nums2[j]+ max(0, dp[i-1][j-1]) - Skip nums1[i]:
dp[i-1][j] - Skip nums2[j]:
dp[i][j-1]
- Take both:
- Initialize dp with a very small value to ensure at least one pair is chosen.
- The answer is
dp[m-1][n-1].
Code
C++
class Solution {
public:
int maxDotProduct(vector<int>& nums1, vector<int>& nums2) {
int m = nums1.size(), n = nums2.size();
vector<vector<int>> dp(m, vector<int>(n, INT_MIN));
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
int prod = nums1[i] * nums2[j];
dp[i][j] = max({prod, (i > 0 && j > 0 ? max(0, dp[i-1][j-1]) + prod : prod),
(i > 0 ? dp[i-1][j] : INT_MIN),
(j > 0 ? dp[i][j-1] : INT_MIN)});
}
}
return dp[m-1][n-1];
}
};
Go
func maxDotProduct(nums1 []int, nums2 []int) int {
m, n := len(nums1), len(nums2)
dp := make([][]int, m)
for i := range dp {
dp[i] = make([]int, n)
for j := range dp[i] {
dp[i][j] = -1 << 30
}
}
for i := 0; i < m; i++ {
for j := 0; j < n; j++ {
prod := nums1[i] * nums2[j]
dp[i][j] = max(prod, prod+max(0, get(dp, i-1, j-1)), get(dp, i-1, j), get(dp, i, j-1))
}
}
return dp[m-1][n-1]
}
func get(dp [][]int, i, j int) int {
if i < 0 || j < 0 {
return -1 << 30
}
return dp[i][j]
}
func max(a ...int) int {
ans := a[0]
for _, v := range a {
if v > ans {
ans = v
}
}
return ans
}
Java
class Solution {
public int maxDotProduct(int[] nums1, int[] nums2) {
int m = nums1.length, n = nums2.length;
int[][] dp = new int[m][n];
for (int i = 0; i < m; ++i)
Arrays.fill(dp[i], Integer.MIN_VALUE);
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
int prod = nums1[i] * nums2[j];
dp[i][j] = Math.max(prod, Math.max(prod + Math.max(0, (i > 0 && j > 0 ? dp[i-1][j-1] : 0)),
Math.max(i > 0 ? dp[i-1][j] : Integer.MIN_VALUE,
j > 0 ? dp[i][j-1] : Integer.MIN_VALUE)));
}
}
return dp[m-1][n-1];
}
}
Kotlin
class Solution {
fun maxDotProduct(nums1: IntArray, nums2: IntArray): Int {
val m = nums1.size
val n = nums2.size
val dp = Array(m) { IntArray(n) { Int.MIN_VALUE } }
for (i in 0 until m) {
for (j in 0 until n) {
val prod = nums1[i] * nums2[j]
dp[i][j] = maxOf(prod,
prod + maxOf(0, if (i > 0 && j > 0) dp[i-1][j-1] else 0),
if (i > 0) dp[i-1][j] else Int.MIN_VALUE,
if (j > 0) dp[i][j-1] else Int.MIN_VALUE)
}
}
return dp[m-1][n-1]
}
}
Python
class Solution:
def maxDotProduct(self, nums1: list[int], nums2: list[int]) -> int:
m, n = len(nums1), len(nums2)
dp = [[float('-inf')] * n for _ in range(m)]
for i in range(m):
for j in range(n):
prod = nums1[i] * nums2[j]
dp[i][j] = max(prod,
prod + max(0, dp[i-1][j-1] if i > 0 and j > 0 else 0),
dp[i-1][j] if i > 0 else float('-inf'),
dp[i][j-1] if j > 0 else float('-inf'))
return dp[m-1][n-1]
Rust
impl Solution {
pub fn max_dot_product(nums1: Vec<i32>, nums2: Vec<i32>) -> i32 {
let m = nums1.len();
let n = nums2.len();
let mut dp = vec![vec![i32::MIN; n]; m];
for i in 0..m {
for j in 0..n {
let prod = nums1[i] * nums2[j];
dp[i][j] = prod.max(prod + dp.get(i.wrapping_sub(1)).and_then(|row| row.get(j.wrapping_sub(1))).copied().unwrap_or(0).max(0))
.max(dp.get(i.wrapping_sub(1)).and_then(|row| row.get(j)).copied().unwrap_or(i32::MIN))
.max(dp.get(i).and_then(|row| row.get(j.wrapping_sub(1))).copied().unwrap_or(i32::MIN));
}
}
dp[m-1][n-1]
}
}
TypeScript
class Solution {
maxDotProduct(nums1: number[], nums2: number[]): number {
const m = nums1.length, n = nums2.length;
const dp = Array.from({length: m}, () => Array(n).fill(-Infinity));
for (let i = 0; i < m; ++i) {
for (let j = 0; j < n; ++j) {
const prod = nums1[i] * nums2[j];
dp[i][j] = Math.max(prod,
prod + Math.max(0, i > 0 && j > 0 ? dp[i-1][j-1] : 0),
i > 0 ? dp[i-1][j] : -Infinity,
j > 0 ? dp[i][j-1] : -Infinity);
}
}
return dp[m-1][n-1];
}
}
Complexity
- ⏰ Time complexity:
O(mn)— We fill a 2D DP table for all pairs. - 🧺 Space complexity:
O(mn)— For the DP table.