Maximum Product Subarray – Solution & Complexity

Solution Walkthrough

1. Understand why sign changes matter

  • Products behave differently from sums because multiplying by a negative flips the sign.
  • A very small negative running product can become the best positive answer after another negative value.

2. Brute-force every starting point

  • Start each subarray at every index and keep multiplying as you extend the right edge.
  • This checks all O(n^2) contiguous subarrays and is correct, but too slow for long inputs.
def maximum_product_subarray(nums):
    best = nums[0]
    for start in range(len(nums)):
        product = 1
        for end in range(start, len(nums)):
            product *= nums[end]
            best = max(best, product)
    return best

3. Keep both extremes at each position

  • Let max_ending be the largest product ending at the current index.
  • Let min_ending be the smallest product ending there, because a future negative number may flip it into the new maximum.

4. Update the running max and min in one pass

  • When the current number is negative, swap the running extremes before multiplying.
  • Then decide whether to start fresh at num or extend the previous product.
def maximum_product_subarray(nums):
    max_ending = nums[0]
    min_ending = nums[0]
    best = nums[0]
    for num in nums[1:]:
        if num < 0:
            max_ending, min_ending = min_ending, max_ending
        max_ending = max(num, max_ending * num)
        min_ending = min(num, min_ending * num)
        best = max(best, max_ending)
    return best

5. Dry run / state trace

Trace nums = [2, 3, -2, 4].

nummax_ending beforemin_ending beforemax_ending aftermin_ending afterbest
222222
322636
-263-2-126
4-2-124-486

The negative -2 destroys the running maximum but creates a much smaller negative product, which is exactly why the minimum track is necessary.

6. Final solution and complexity

Track the largest and smallest products ending at each index; one pass gives O(n) time and O(1) space.

def maximum_product_subarray(nums: list[int]) -> int:
    max_ending = nums[0]
    min_ending = nums[0]
    best = nums[0]
    for num in nums[1:]:
        if num < 0:
            max_ending, min_ending = min_ending, max_ending
        max_ending = max(num, max_ending * num)
        min_ending = min(num, min_ending * num)
        best = max(best, max_ending)
    return best

FAQ