Skip to content
dsa450for five year olds

Dynamic Programming · 36 of 60

Smallest sum contiguous subarray

Find the run of days in a row where your sticker count dropped the most, which is the stretch of an array with the smallest sum.

Simple way
time O(n^2)
room O(1)
Clever way
time O(n)
room O(1)

The problem, simply

You get a list of whole numbers, some positive and some negative. Look at every stretch of numbers that sit next to each other (at least one number). Give back the smallest sum any stretch can have.

It is the mirror image of the famous "largest sum stretch" problem.

A story to remember it

Picture it

arraysticker change per day
0123456sum -63-42-3-17-5

Days 1 to 4 add up to -4 + 2 - 3 - 1 = -6. No other stretch is smaller. Note that the stretch keeps the +2 inside, because the numbers around it make up for it.

  • answer

Examples

Mixed numbers

In
[3, -4, 2, -3, -1, 7, -5]
Out
-6

The stretch -4, 2, -3, -1.

All positive

In
[2, 6, 8, 1, 4]
Out
1

Adding more positive numbers only makes the sum bigger, so the best stretch is the single smallest number.

All negative

In
[-1, -2, -3]
Out
-6

Every number pulls the sum down, so take them all.

One number

In
[5]
Out
5

The only stretch is the number itself.

Ties

In
[1, -1, 1, -1]
Out
-1

Several stretches reach -1, such as [-1] and [-1, 1, -1]. Only the sum is asked.

Way 1: the simple way

Try every start. From each start, walk right and keep a running sum. Every time the running sum is a new low, remember it.

  1. For each start i, set sum = 0.
  2. For each end j from i onward, add a[j] and compare sum with the smallest seen.
arraystart i = 1, walking j to the right
0123456running sum -63-42-3-17-5ji

Starting at day 1 and walking right: -4, then -2, then -5, then -6, then 1, then -4. The low point -6 is at day 4. Then we start again at day 2, and so on.

java
class Solution {
    public int smallestSumSubarray(int[] a) {
        int best = Integer.MAX_VALUE;
        for (int i = 0; i < a.length; i++) {        // every start
            int sum = 0;
            for (int j = i; j < a.length; j++) {    // every end
                sum += a[j];
                best = Math.min(best, sum);
            }
        }
        return best;
    }
}

How long it takes: about n * n / 2 stretches, O(n^2). Extra room: two numbers, O(1).

Way 2: the clever way

Keep the worst streak that ends today, and the worst streak ever.

  1. Start both at a[0].
  2. For each next number x: endHere = min(x, endHere + x). Either start fresh with x, or carry yesterday's streak.
  3. best = min(best, endHere).

Each endHere only needs the one before it, so this is a tiny DP with one box of memory.

One pass over the days, keeping the worst streak ending here

01 / 08

arraysticker change per day
0123456sum 33-42-3-17-5
arraysmallest sum ending here
01234563

Day 0: the only stretch ending here is 3 itself. Smallest so far is 3.

ending here
3
smallest
3
  • looking now
  • checking
  • answer
  • just added
  • ignored
java
class Solution {
    public int smallestSumSubarray(int[] a) {
        int endHere = a[0];      // smallest sum of a stretch ending at this day
        int best = a[0];         // smallest sum seen anywhere
        for (int i = 1; i < a.length; i++) {
            endHere = Math.min(a[i], endHere + a[i]);  // start fresh or carry on
            best = Math.min(best, endHere);
        }
        return best;
    }
}

How long it takes: one walk, O(n). Extra room: two numbers, O(1).

Watch out for

  • The stretch must be at least one number long. An empty stretch with sum 0 does not count.
  • Resetting to 0 instead of to a[i]. The textbook "reset when positive" version works only if you compare before resetting. min(a[i], endHere + a[i]) avoids the trap.
  • Very large sums can overflow int. Use long if numbers can be near a billion.

What interviewers ask next

  • Can you reuse the largest sum code? Yes. Flip the sign of every number, find the largest sum stretch, and flip the answer back.
  • Return the start and end of the stretch. Remember where the current streak started each time you start fresh, and copy it when you find a new best.
  • Maximum sum in a circular array (LeetCode 918). The best wrap around stretch is total - smallest stretch. So you need exactly this problem.
  • Smallest sum stretch of length at least k. Use running totals (prefix sums) and keep the largest prefix that is at least k positions back.

Remember this

Grown-up words: a stretch is a contiguous subarray, the worst streak ending today is the DP state minEndingHere, the whole trick is Kadane's algorithm, and running totals are prefix sums.