Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 24 – Dynamic programming: foundations

Maximum subarray: sum, product and circular

The stretch of consecutive values with the largest total: the maximum subarray sum, the maximum product with negatives and zeros, and the circular case.

  • Beginner
  • 35 minutes
  • Examples run with Python 3.14.8, Pyodide 314.0.7, Node.js 24.21.0, quickjs 0.32.0, Java 25.0.4.1, Clang 21.0.0 and Go 1.27.1
  • By MySmartCoPilot
Free intro, Pro: 8 of 35 minutes free
Free: Three problems on one row of numbers · The brute force, and why it fails · What to notice. With a pass: the other 12 sections and 3 interview answers, about 27 minutes.

What you will learn

  • Compute the maximum subarray sum in one pass with Kadane's algorithm
  • Prove the algorithm with an invariant about the best stretch ending at each position
  • Report the days of the best stretch, and handle arrays where every value is negative
  • Compute the maximum product by carrying both the largest and the smallest product
  • Solve the circular version, where a stretch may wrap around the end

Before you start

What you’ll learn · Before you start

What you will learn

  • Compute the maximum subarray sum in one pass with Kadane's algorithm
  • Prove the algorithm with an invariant about the best stretch ending at each position
  • Report the days of the best stretch, and handle arrays where every value is negative
  • Compute the maximum product by carrying both the largest and the smallest product
  • Solve the circular version, where a stretch may wrap around the end

Before you start

On this page

The maximum subarray problem asks for the stretch of consecutive values with the largest total, such as the best run of days in a shop’s profit-and-loss ledger. Kadane’s algorithm solves it in one pass by keeping the best total of a stretch that ends at the current position, which is why it belongs with linear DP. Two variants come up often. The maximum product of a stretch changes what must be kept, because a negative value turns the smallest product into the largest and a zero ends every stretch through it. The circular version lets a stretch wrap from the last value to the first. This lesson poses all three on original data.

Three problems on one row of numbers

Problem A, the best stretch of a ledger. A shop records each day’s profit or loss. Return the largest total of a non-empty stretch of consecutive days. Limits: 1 ≤ n ≤ 200,000 and −10,000 ≤ ledger[i] ≤ 10,000.

  • [3, −5, 4, −1, 2, 6, −8, 1] gives 11, from days 2 to 5.
  • [−7, −2, −9] gives −2: a loss on every day, so the best stretch is the single smallest loss.

Problem B, the best product of a run of cards. A row of up to 200,000 number cards from −9 to 9; return the largest product of a run of consecutive cards. Every product of a run fits in a signed 64-bit integer.

  • [2, −3, −2, 4, 0, −5] gives 48, from the first four cards.
  • [−2, 0, −1] gives 0, the 0 on its own.

Problem C, a ledger in a ring. The same as A, but the last day is followed by the first, so a stretch may wrap. [5, −8, 2, 4, −9, 6] gives 11, from the last day and the first: 6 + 5.

Three rows of numbers with the best stretch of each marked: days 2 to 5 of a ledger, cards 0 to 3 for a product, and a wrapped stretch.A ledger: the best stretch3−54−126−814 − 1 + 2 + 6 = 11, days 2 to 5Cards: the best product2−3−240−52 × (−3) × (−2) × 4 = 48A ledger in a ring5−824−966 + 5 = 11: the dashed link wraps the last dayround to the first

The three problems, with the best stretch of each

Text description of the diagram

The diagram has three rows of boxes, one number per box. The boxes of each row's best stretch have a double border.

  1. The ledger 3, −5, 4, −1, 2, 6, −8, 1. The best stretch is 4, −1, 2, 6 (days 2 to 5), with a total of 11. It includes the loss of 1, because the profits on both sides of it are larger.
  2. The cards 2, −3, −2, 4, 0, −5. The best run for a product is 2, −3, −2, 4 (cards 0 to 3), with a product of 48: the two negative cards cancel out. The 0 ends every run that crosses it.
  3. The ledger 5, −8, 2, 4, −9, 6, read as a ring: a dashed link runs from the last day back to the first. The best stretch wraps from the last day to the first: 6 and then 5, with a total of 11. Without wrapping, the best stretch would be 2, 4 or the single 6, a total of 6.

The brute force, and why it fails

Try every start, and extend the stretch one value at a time while keeping its total or product:

Every stretch tried, with the stretches counted Python · brute/stretch_brute.py
def best_sum_brute(ledger):
    """Every stretch of consecutive days, its total kept as the end moves right: n(n + 1) / 2 stretches."""
    best, stretches = None, 0
    for start in range(len(ledger)):
        total = 0
        for end in range(start, len(ledger)):
            total += ledger[end]
            stretches += 1
            best = total if best is None else max(best, total)
    return best, stretches


def best_product_brute(cards):
    """The same for products: every run of consecutive cards, its product kept as the run grows."""
    best = None
    for start in range(len(cards)):
        product = 1
        for end in range(start, len(cards)):
            product *= cards[end]
            best = product if best is None else max(best, product)
    return best


for ledger in ([3, -5, 4, -1, 2, 6, -8, 1], [-7, -2, -9]):
    best, stretches = best_sum_brute(ledger)
    print(f"ledger {ledger}: best stretch {best} ({stretches} stretches tried)")
for cards in ([2, -3, -2, 4, 0, -5], [-2, 0, -1], [-3]):
    print(f"cards {cards}: best product {best_product_brute(cards)}")
n = 200_000
print(f"{n:,} days: {n * (n + 1) // 2:,} stretches")

Output

ledger [3, -5, 4, -1, 2, 6, -8, 1]: best stretch 11 (36 stretches tried)
ledger [-7, -2, -9]: best stretch -2 (6 stretches tried)
cards [2, -3, -2, 4, 0, -5]: best product 48
cards [-2, 0, -1]: best product 0
cards [-3]: best product -3
200,000 days: 20,000,100,000 stretches

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 stretch_brute.py

There are n(n + 1)/2 stretches: 36 for the first ledger, but 20,000,100,000 for 200,000 days, minutes of work even in C++. Recomputing each sum from scratch would cost another factor of n.

What to notice

  • A stretch that ends on day i either extends the best stretch ending on day i − 1 or starts on day i. One number per day is enough to decide between them.
  • For products, a negative card turns the smallest product into the largest, so the smallest must be carried too, and a 0 ends every run that crosses it.
  • A stretch that wraps around leaves out one stretch in the middle, so the circular best is tied to the worst stretch.

Kadane’s algorithm, and the invariant that proves it Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • The days of the best stretch, traced
  • Ledgers of losses only: the stretch must not be empty
  • Products: carry the largest and the smallest
  • A weaker promise: when only the answer fits in 64 bits
  • Around the season: the total minus the worst stretch
  • The code in five languages
  • Four bugs and the smallest inputs that break them
  • Exercise
    • The best stretch of a ledger that wraps around
  • Check yourself
  • A practice set with sample answers
  • Interview questions
    • Prove that Kadane's algorithm returns the largest total of a non-empty stretch, using an invariant. Then say what changes when the problem allows the empty stretch, and what a common version returns on an array where every value is negative.
    • For the maximum product of a run, the statement promises only that the answer fits in a signed 64-bit integer. Show how the usual solution that carries the largest and the smallest product can return a wrong answer in Java or Go, and fix it without arbitrary-precision numbers.
    • Find the best stretch of a circular array, where a stretch may wrap from the last value to the first, in one pass. Prove your method, say which inputs need a special case, and explain how to return the start and the length of the best stretch.
  • Key takeaways

8 runnable examples (C++, Go, Java, JavaScript, Python) · 1 exercise with sample tests · 3 interview answers · about 23 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.