Dynamic Programming · Classic DP Patterns

DP: Kadane's Variants

Extending Kadane's running-sum idea to products by tracking both a running maximum AND a running minimum ending at each position, since multiplying by a negative number can flip the smallest product seen so far into the largest.

  • 5 min read
  • 8 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: subarrays that wrap around
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

Kadane's algorithm finds the maximum-sum subarray by keeping one running value. At each element, it either extends the best subarray ending just before, or starts fresh. That works because adding the same number to two running sums never changes which one is larger. Change the operation or the shape and that single value isn't enough anymore. With products, a large negative can flip into the best answer. With a circular array, the best subarray can wrap around the end. This skill covers the extra state each variant needs, while keeping the single O(n) pass.

We'll go from simple to more technical:

  1. The pattern: maximum product, tracking both a running max and a running min.
  2. A second flavor: circular arrays, using the total minus the smallest middle part.
  3. Where streak-style DP shows up in production.
  4. What breaks it: a missing min, the all-negative case, and overflow.

The approach

Java

int maxProduct(int[] nums) {
    int best = nums[0], curMax = nums[0], curMin = nums[0];   // best and worst product ENDING here
    for (int i = 1; i < nums.length; i++) {
        int x = nums[i];
        if (x < 0) { int t = curMax; curMax = curMin; curMin = t; }   // a negative flips best and worst
        curMax = Math.max(x, curMax * x);                           // extend, or start fresh at x
        curMin = Math.min(x, curMin * x);
        best = Math.max(best, curMax);
    }
    return best;
}

Python

def max_product(nums: list[int]) -> int:
    best = cur_max = cur_min = nums[0]          # best and worst product ENDING here
    for x in nums[1:]:
        if x < 0:
            cur_max, cur_min = cur_min, cur_max  # a negative flips best and worst
        cur_max = max(x, cur_max * x)            # extend, or start fresh at x
        cur_min = min(x, cur_min * x)
        best = max(best, cur_max)
    return best

Multiplying by a negative turns the most negative product into the largest one, so the algorithm keeps both extremes ending at each position and swaps them when x is negative. On [-2, 3, -4], tracking only the maximum gives 3; tracking both finds -2 * 3 * -4 = 24. Zeros are handled by "start fresh at x": a product that hits 0 restarts at the next element.

A second flavor: subarrays that wrap around

In a circular array, a subarray may run off the end and continue at the start. A wrapping subarray is everything except some contiguous middle part - so its best sum is the total minus the smallest middle subarray, which is Kadane run for the minimum:

Java

int total = 0, curMax = 0, bestMax = Integer.MIN_VALUE, curMin = 0, bestMin = Integer.MAX_VALUE;
for (int x : nums) {
    total += x;
    curMax = Math.max(x, curMax + x);  bestMax = Math.max(bestMax, curMax);
    curMin = Math.min(x, curMin + x);  bestMin = Math.min(bestMin, curMin);
}
if (bestMax < 0) return bestMax;              // all negative: the "complement" would be empty
return Math.max(bestMax, total - bestMin);

Python

def max_subarray_sum_circular(nums: list[int]) -> int:
    total = cur_max = cur_min = 0
    best_max, best_min = float("-inf"), float("inf")
    for x in nums:
        total += x
        cur_max = max(x, cur_max + x)
        best_max = max(best_max, cur_max)
        cur_min = min(x, cur_min + x)
        best_min = min(best_min, cur_min)
    if best_max < 0:
        return best_max                          # all negative: the "complement" would be empty
    return max(best_max, total - best_min)

The all-negative check matters: for [-3, -2, -3], the smallest middle part is the whole array, and total - bestMin = 0 describes the empty subarray, which isn't allowed. The answer is -2.

Where you'll see it

Signal and time-series analysis looks for the strongest contiguous window (the best stretch of returns, the highest-gain run of readings), and periodic data - hours of a day, days of a week - makes windows wrap around. Compounded growth rates multiply rather than add, which is where the min/max tracking of the product version applies.

Which tool for which job?

You needUse
Maximum-sum subarrayKadane: extend or restart
Maximum-product subarrayrunning max and min, swapped on a negative
A cross-check for productsbest prefix or suffix product per zero-free segment
Maximum sum in a circular arraymax(Kadane max, total - Kadane min), unless all negative
Maximum sum with at most one element deletedtwo states: nothing deleted yet, one deleted
Maximum-sum rectangle in a matrixKadane over the column sums of each row pair

Common mistakes

MistakeWhat happensDo this instead
Tracking only the running max for products[-2, 3, -4] gives 3 instead of 24track the min too, and swap on a negative
No all-negative check in the circular versionreturns 0, the empty subarrayreturn the max element when it's negative
Assuming products fit a long10^20 wraps to 7766279631452241920rely on the problem's bound, or use logs
Products of doubles10^400 becomes Infinitycompare sums of logarithms

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why does the maximum product subarray need a running minimum as well as a running maximum?

Show answer

A negative number turns the most negative running product into the largest. On [-2, 3, -4], the product ending at 3 has minimum -6; multiplying by -4 gives 24, the answer. Tracking only the maximum (3) loses that.

2. How can the best wrapping subarray be found without trying every start and length?

Show answer

A wrapping subarray is the whole array minus a contiguous middle part. Its best sum is total minus the minimum subarray sum (Kadane for the minimum). Compare it with the normal maximum subarray and take the larger.

3. For [-3, -2, -3], the circular formula gives total - minSubarray = 0. Why is that wrong?

Show answer

The minimum subarray is the whole array, so its complement is empty - and the problem requires a non-empty subarray. When the normal maximum is negative (every element is negative), return it directly: -2.

Key takeaways

  • Kadane keeps the best subarray ending at each position: extend it or start fresh.
  • For products, keep the best and worst ending here and swap them on a negative.
  • A wrapping subarray is the complement of a middle one: total - minimum subarray.
  • Guard the all-negative case - the complement of everything is empty.
  • Still O(n) time and O(1) space.

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Max productrunning max and min
Circular max sumtotal - min subarray (all-negative check)
One deletion allowedkeep / dropped states
Matrix rectangleKadane over row pairs
Huge productslog-sums plus a sign count

Snippets worth knowing by heart

Java

int best = nums[0], hi = nums[0], lo = nums[0];
for (int i = 1; i < nums.length; i++) {
    int x = nums[i];
    if (x < 0) { int t = hi; hi = lo; lo = t; }      // a negative flips the extremes
    hi = Math.max(x, hi * x);
    lo = Math.min(x, lo * x);
    best = Math.max(best, hi);
}

Python

keep, dropped, best = a[0], float("-inf"), a[0]    # at most one deletion
for x in a[1:]:
    dropped = max(dropped + x, keep)
    keep = max(keep + x, x)
    best = max(best, keep, dropped)

The traps

  • No running min for products: [-2, 3, -4] gives 3 instead of 24. Track the min too, and swap on a negative.
  • All-negative circular arrays: total minus min returns 0, the empty subarray. Return the max element instead.
  • long or double overflow: products wrap or become Infinity. Rely on the problem's bound, or compare sums of logs.

Staff-level points, one line each

  • Max product: min/max tracking, the prefix/suffix scan and brute force agreed on 2,000 arrays.
  • One deletion: the two-state DP matched brute force on 2,000 arrays.
  • 30 × 30 rectangle: 13,950 steps with Kadane over row pairs against 216,225 for every rectangle; both gave 179.
  • 10^20 wrapped in a long, and 10^400 became Infinity in a double. Use log-sums when there's no bound.

Say it in an interview

  • "A negative can turn the smallest product into the largest, so I track both."
  • "A wrapping subarray is the total minus a middle part, so I run Kadane for the minimum too."
  • "Each allowed exception adds a state."
  • "For a matrix I fix two rows and run Kadane on the column sums."

Practice

This skill comes with 8 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice DP: Kadane's Variants free

Max Product Subarray

MediumProCoding problemJava · Python

Choose a run of one or more consecutive numbers from nums whose product is as large as possible, and return that product.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Maximum Sum Circular Subarray

MediumProCoding problemJava · Python

Treat nums as a ring: after the last element comes the first one again. Return the largest sum of a run of one or more consecutive elements, where a run may wrap around the end but can't use any element twice.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice DP: Kadane's Variants free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime