Skip to content
DSAPrep

    ↑↓ to move · Enter to open · Esc to close

    Medium1-D DP

    Maximum Product Subarray

    Given an integer array nums, find a subarray that has the largest product, and return the product.

    The test cases are generated so that the answer will fit in a 32-bit integer. The product of an array with a single element is the value of that element.

    Example 1

    Input: nums = [2,3,-2,4]

    Output: 6

    Explanation: [2,3] has the largest product 6.

    Example 2

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

    Output: 0

    Explanation: The result cannot be 2, because [-2,-1] is not a contiguous subarray (0 sits between them).

    Constraints

    • 1 <= nums.length <= 2 * 10^4
    • -10 <= nums[i] <= 10
    • The product of any subarray of nums is guaranteed to fit in a 32-bit integer.
    View original on LeetCode ↗

    Kadane’s algorithm for maximum sum subarray does not directly work for products, because multiplying by a negative number flips the sign – the smallest (most negative) product ending at i-1 can become the largest product ending at i if nums[i] is negative. So track two running values at each position: maxDP[i], the largest product of a subarray ending exactly at i, and minDP[i], the smallest (most negative). Both are needed because either one might flip into the other’s role on the next step.

    Brute Force

    Time O(n²)Space O(1)

    Try every subarray directly: for each starting index, extend the ending index one step at a time, keeping a running product, and track the best seen.

    class Solution:
    def maxProduct(self, nums: list[int]) -> int:
    n = len(nums)
    best = nums[0]
    for i in range(n):
    prod = 1
    for j in range(i, n):
    prod *= nums[j]
    best = max(best, prod)
    return best

    O(n²) subarrays, each extended in O(1) amortized work by reusing the running product → O(n²) time, O(1) space.

    Bottom-Up DP (Track Max and Min)

    OptimalTime O(n)Space O(n)

    At each index, the best subarray ending there either starts fresh at nums[i], or extends the previous best-max subarray, or extends the previous best-min subarray (which flips sign into a new max if nums[i] is negative). Take the max and min of all three candidates.

    class Solution:
    def maxProduct(self, nums: list[int]) -> int:
    maxDP = [0] * len(nums)
    minDP = [0] * len(nums)
    maxDP[0] = minDP[0] = nums[0]
    ans = nums[0]
    for i in range(1, len(nums)):
    candidates = (nums[i], maxDP[i - 1] * nums[i], minDP[i - 1] * nums[i])
    maxDP[i] = max(candidates)
    minDP[i] = min(candidates)
    ans = max(ans, maxDP[i])
    return ans

    Watch how the previous minimum is as important as the previous maximum: a negative number turns the smallest product into the largest candidate.

    Trace for nums = [2, 3, -2, 4] (both maxDP and minDP tables are shown, alongside the three candidates that produce each entry):

    nums
    2
    3
    -2
    4
    maxDP
    2
    ·
    ·
    ·
    minDP
    2
    ·
    ·
    ·

    Candidates

    start fresh—
    via max—
    via min—
    Chosenmax ending here = 2min ending here = 2answer = 2
    1 / 7
    currentcomparingresult

    Index 0: the best and worst subarray ending here are both nums[0] = 2, so the answer starts at 2.

    One pass, constant work per index → O(n) time. The full maxDP/minDP arrays cost O(n) space here, though only the previous entry of each is ever needed, so this can be reduced to O(1) space with two rolling variables.

    More 1-D DP practice