The largest product of a subarray
Given a list of integers nums, return the largest product of a non-empty contiguous subarray.
Examples
| Input | Output | Why |
|---|---|---|
nums = [2,3,-2,4] | 6 | 2 · 3. |
nums = [-2,3,-4] | 24 | Two negatives make a positive. |
nums = [-2,0,-1] | 0 | The zero on its own. |
Constraints
- 1 ≤ nums.length ≤ 2 · 10⁴
- −10 ≤ nums[i] ≤ 10
- Every product fits in a 64-bit integer
Hints
Hint 1
The best product ending here could come from the worst product ending at the previous position, if the current number is negative.
Hint 2
Keep hi and lo: the largest and smallest products ending at i.
Hint 3
hi, lo = max(x, hi·x, lo·x), min(x, hi·x, lo·x).
Where people lose marks
- Tracking only the maximum, which fails on [−2, 3, −4].
- Updating hi before computing the new lo from the old hi.
- Starting the answer at 0 or 1 rather than at nums[0].
Dry run on [-2,3,-4]
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 3nums-2031-42hi-2lo-2best-2
Start: hi = lo = -2.
-
Step 2 / 3nums-2031-42hi3lo-6best3
x = 3: candidates 3, -6, -6; hi = 3, lo = -6.
-
Step 3 / 3nums-2031-42hi24lo-12best24
x = -4: candidates -4, -12, 24; hi = 24, lo = -12. The answer is 24.
How to think about it
The best-ending-here pattern, with one twist: multiplying by a negative number turns the smallest product into the
largest. So keep both the largest (hi) and the smallest (lo) product of a subarray ending at the
current position. Each new number x either starts a new subarray or extends one of those two, giving three candidates for
each: x, hi · x, lo · x. A zero resets both to zero naturally.
Complexity. Time O(n) · Space O(1)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
def max_product(nums):
hi = lo = best = nums[0] # largest and smallest product ending here
for x in nums[1:]:
candidates = (x, hi * x, lo * x)
hi, lo = max(candidates), min(candidates) # both from the old hi and lo
best = max(best, hi)
return best#include <algorithm>
#include <vector>
long long solve(const std::vector<int>& nums) {
long long hi = nums[0], lo = nums[0], best = nums[0];
for (size_t i = 1; i < nums.size(); ++i) {
long long x = nums[i], a = hi * x, b = lo * x;
hi = std::max({x, a, b}); // a negative x swaps the roles
lo = std::min({x, a, b});
best = std::max(best, hi);
}
return best;
}class Solution {
public long maxProduct(int[] nums) {
long hi = nums[0], lo = nums[0], best = nums[0];
for (int i = 1; i < nums.length; i++) {
long x = nums[i], a = hi * x, b = lo * x;
hi = Math.max(x, Math.max(a, b)); // a negative x swaps the roles
lo = Math.min(x, Math.min(a, b));
best = Math.max(best, hi);
}
return best;
}
}function maxProduct(nums) {
let hi = nums[0], lo = nums[0], best = nums[0];
for (let i = 1; i < nums.length; i++) {
const x = nums[i], a = hi * x, b = lo * x;
hi = Math.max(x, a, b); // a negative x swaps the roles
lo = Math.min(x, a, b);
best = Math.max(best, hi);
}
return best;
}fn solve(nums: &[i64]) -> i64 {
let (mut hi, mut lo, mut best) = (nums[0], nums[0], nums[0]);
for &x in &nums[1..] {
let (a, b) = (hi * x, lo * x);
hi = x.max(a).max(b); // a negative x swaps the roles
lo = x.min(a).min(b);
best = best.max(hi);
}
best
}solve :: [Int] -> Int
solve (x : xs) = (\(best, _, _) -> best) (foldl step (x, x, x) xs)
where
-- (best so far, largest ending here, smallest ending here)
step (best, hi, lo) y = let c = [y, hi * y, lo * y]; hi' = maximum c in (max best hi', hi', minimum c)
solve [] = 0
Variants to try
- Return the subarray itself.
- The product must be computed modulo a prime: maximisation no longer makes sense; count instead.
- Use logarithms for very large magnitudes (with sign tracked separately).
Next problems
The largest-sum subarray
Return the largest sum of a non-empty contiguous subarray: the best sum ending at each position, extend or restart.
Open the problem → Easy10 minOne trade: the best profit
Buy on one day and sell on a later day. The best profit from selling today is today's price minus the lowest price so far.
Open the problem → Medium20 minThe largest sum on a circle
The list wraps around. The best stretch either does not wrap (ordinary Kadane) or does, and then it is the total minus the worst stretch.
Open the problem →Write a solution and run it against the real test table.
Keep going
The largest-sum subarray
Return the largest sum of a non-empty contiguous subarray: the best sum ending at each position, extend or restart.
Open the problem → Easy10 minOne trade: the best profit
Buy on one day and sell on a later day. The best profit from selling today is today's price minus the lowest price so far.
Open the problem → Medium20 minThe largest sum on a circle
The list wraps around. The best stretch either does not wrap (ordinary Kadane) or does, and then it is the total minus the worst stretch.
Open the problem → Dynamic programmingThe pattern behind it
Write the recursion, notice the repeats, fill a table in an order that makes each entry final.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.