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).
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] <= 1000Problem Overview: You are given two integer arrays. Pick a non-empty subsequence from each array so that their dot product is maximized. The subsequences must maintain original order, but you can skip elements. Negative values make the problem tricky because skipping everything except the best pair may still produce the optimal result.
Approach 1: Recursive Backtracking with Memoization (Time: O(n*m), Space: O(n*m))
Start with recursion that explores three choices at every pair of indices (i, j): take both elements and include their product, skip the element in the first array, or skip the element in the second array. The recursive relation becomes max(nums1[i] * nums2[j] + solve(i+1, j+1), solve(i+1, j), solve(i, j+1)). Since many states repeat, store results in a memo table keyed by (i, j). Memoization reduces exponential recursion to a manageable O(n*m) dynamic programming state space while preserving the intuitive recursive logic.
This approach works well when you want to reason about the decision tree first. Each state represents the best possible dot product using suffixes of the two arrays. Handling negative values is important: the product at the current step may start a new subsequence instead of extending the previous one.
Approach 2: Dynamic Programming with Memoization (Time: O(n*m), Space: O(n*m))
Model the problem as a classic dynamic programming state. Let dp[i][j] represent the maximum dot product using elements starting from index i in nums1 and j in nums2. For each pair, compute the product nums1[i] * nums2[j]. You either start a new subsequence with that product or extend the best subsequence from dp[i+1][j+1]. Also consider skipping elements via dp[i+1][j] or dp[i][j+1].
The recurrence becomes dp[i][j] = max(product, product + dp[i+1][j+1], dp[i+1][j], dp[i][j+1]). This guarantees the subsequence remains non-empty while accounting for negative combinations. Since every pair of indices is computed once, the complexity stays O(n*m). Arrays and index transitions make this a standard grid-style DP similar to problems on arrays and sequence alignment.
Recommended for interviews: Interviewers typically expect the dynamic programming formulation. Start by explaining the recursive decision process, then introduce memoization to eliminate repeated states. Showing the transition from recursion to DP demonstrates strong understanding of dynamic programming patterns and sequence comparison problems.
This approach involves using dynamic programming to store results of subproblems and avoid redundant calculations.
We define a recursive helper function that considers each element from both arrays and decides whether to include it in the subsequence or not. The dynamic programming table stores the maximum dot product up to the current indices.
This C implementation uses a 2D array dp where dp[i][j] stores the maximum dot product of subsequences up to the i-th index of nums1 and j-th index of nums2.
Time Complexity: O(m * n) where m and n are the sizes of the two arrays.
Space Complexity: O(m * n) for the dp table.
This approach involves using backtracking combined with memoization to efficiently enumerate possible subsequences, storing results for reused subsequence comparisons to enhance performance.
The memoization helps to prevent recalculating results for the same indices, transforming exponential time complexity to polynomial time complexity.
This C implementation uses a recursive helper function to explore each possible state of subsequences. The dp array is used to cache results of subproblems, reducing the need for redundant calculations.
Time Complexity: O(m * n) where m and n are the lengths of nums1 and nums2. Although recursive, memoization reduces repeated work.
Space Complexity: O(m * n) for the memoization array.
We define f[i][j] to represent the maximum dot product of two subsequences formed by the first i elements of nums1 and the first j elements of nums2. Initially, f[i][j] = -infty.
For f[i][j], we have the following cases:
nums1[i-1] or do not select nums2[j-1], i.e., f[i][j] = max(f[i-1][j], f[i][j-1]);nums1[i-1] and nums2[j-1], i.e., f[i][j] = max(f[i][j], max(0, f[i-1][j-1]) + nums1[i-1] times nums2[j-1]).The final answer is f[m][n].
The time complexity is O(m times n), and the space complexity is O(m times n). Here, m and n are the lengths of the arrays nums1 and nums2, respectively.
| Approach | Complexity |
|---|---|
| Dynamic Programming with Memoization | Time Complexity: O(m * n) where m and n are the sizes of the two arrays. |
| Recursive Backtracking with Memoization | Time Complexity: O(m * n) where m and n are the lengths of nums1 and nums2. Although recursive, memoization reduces repeated work. |
| Dynamic Programming | — |
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Recursive Backtracking with Memoization | O(n*m) | O(n*m) | When reasoning about the decision tree first and converting recursion into DP. |
| Dynamic Programming with Memoization | O(n*m) | O(n*m) | General optimal solution for interview and production scenarios. |
| Bottom-Up Dynamic Programming | O(n*m) | O(n*m) | Preferred when avoiding recursion or stack overhead. |
Max Dot Product of Two Subsequences | Detailed | simplified | Leetcode 1458 | codestorywithMIK • codestorywithMIK • 12,809 views views
Watch 9 more video solutions →Practice Max Dot Product of Two Subsequences with our built-in code editor and test cases.
Practice on FleetCode