2Brute2Best
🔥0

Maximum Product Subarray

Medium·LeetCode #152Basic Anchor
LeetCode
30-Second Mental ModelKadane with two states: max product and min product (Maximum product)
Complexity:O(N^2)→O(N)
Key Insight

Walk left to right and keep two values: curMax, the biggest product of a block that ends at the current index, and curMin, the smallest. A block ending here is either the new number alone or an earlier block times the new number, so the biggest comes from x, x · curMax, or x · curMin. We keep the smallest too, because a negative x turns the smallest product into the biggest.

Core Validity Invariant

The two extremes are enough: the biggest and smallest products ending at i − 1 decide the biggest and smallest products ending at i.

01

Problem

You are given an integer array nums. Look at every contiguous block: a run of numbers that sit next to each other with nothing skipped. The product of a block is all its numbers multiplied together.

Return the largest product that any non-empty block has.

Input

An integer array nums of length n.

Output

A single integer: the largest product of any non-empty contiguous block.

Example 1
Input: nums = [2, 3, -2, 4]
Output: 6

The block [2, 3] gives 6. Adding the -2 makes the product negative: the whole array gives -48, so 6 is the best.

Example 2
Input: nums = [-2, 0, -1]
Output: 0

Any block that contains the 0 has product 0. The single blocks [-2] and [-1] are negative, so the best is 0.

Constraints
  • • 1 ≤ nums.length ≤ 2 × 10^4
  • • −10 ≤ nums[i] ≤ 10
  • • The product of any contiguous block fits in a 32-bit integer.
02

First, think yourself

Before opening any solution, try to answer these on paper. Use hints only when stuck — each hint is counted.

  1. 1.

    For the maximum sum of a block, I kept only the best sum that ends here. Why might that break when I multiply instead of add?

  2. 2.

    If the best product ending at the previous index is very negative, what happens after I multiply it by another negative number?

  3. 3.

    What does a 0 do to every block that contains it?

  4. 4.

    How many blocks end at index i, and can I reuse what I already found for index i − 1?

  5. 5.

    Is there any other block ending at i − 1 that could still matter after the next multiply?

03

Brute force approach

05

Optimized approach

06

Why does this algorithm work?

07

Dry run

08

All approaches compared

ApproachIdeaTimeSpace
Brute ForceMultiply each start's numbers one by one; track the largest productO(N^2)O(1)
OptimalKeep the largest and smallest product of blocks ending here; a negative number swaps their rolesO(N)O(1)
09

Why does this belong to this pattern?

Clues in the problem statement
  • “contiguous block”The answer is a range [l … r], so we walk left to right and keep a running state that ends at each index.
  • “largest product”Multiplying a longer block does not always help, so one running value is not enough.
  • “numbers can be negative”A negative number flips the sign, so the smallest product can become the largest. We must track both.
Contiguous range ending at i+product, not sum+track max AND min because of negatives→Kadane with two states: max product and min product (Maximum product)

This is Kadane's idea with a second state. Plain Kadane keeps only the best sum ending here, which is safe for sums because adding never reverses the order. Multiplying by a negative number does reverse it, so the smallest product must be kept as well. The next question, Maximum Length with Positive Product, walks the same way but keeps lengths instead of product values.

10

Don't make this mistake

Pattern confusion: plain Kadane with products
✗ cur = max(x, cur * x); // only the largest product is tracked
✓ a = x * curMax; b = x * curMin; curMax = max(x, max(a, b)); curMin = min(x, min(a, b));

On the dry run [2, 3, -2, -4, 0, -1, 5] the largest-only version keeps 6 after the 3. Then -2 replaces it with -2, which throws away the -12. At -4 it gets max(-4, -4 · -2) = 8, and the program returns 8. The true answer is 48, from 2 · 3 · (-2) · (-4), and it needs the -12.

Updating curMax before computing curMin
✗ curMax = max(x, x * curMax, x * curMin); curMin = min(x, x * curMax, x * curMin);
✓ long long a = x * curMax, b = x * curMin; curMax = max(x, max(a, b)); curMin = min(x, min(a, b));

On the same dry run, after -2 the new curMax is -2. Then curMin = min(-2, -2 · -2, -2 · 3) = -6 instead of -12. At -4 the code returns max(-4, 8, 24) = 24, not 48. Both values must come from the old state.

Starting curMax at 0
✗ long long curMax = 0, curMin = 0, best = 0;
✓ long long curMax = nums[0], curMin = nums[0], best = nums[0];

With curMax = 0, a negative first number gives max(x, 0) = 0. For nums = [-3] the program returns 0 instead of -3, because the answer must be a real block.

Dropping the single number x
✗ curMax = max(x * curMax, x * curMin);
✓ curMax = max(x, max(x * curMax, x * curMin));

A block can start fresh at x. For nums = [0, 2], both values are 0 after the zero, so without x alone the 2 is never considered and the program returns 0 instead of 2.

11

Want to implement this?

Write it yourself on LeetCode, then come back and compare your code with the solutions above.

Open coding workspace
How did it go?
Beyond The LeetCode Problem

How this concept connects to System Design & C++ Systems

Bridge DSA with production engineering

Top engineering interviews test whether you understand how algorithms like DP — Kadane / Subarray translate into production architectures and low-level memory mechanics.