Back to Arrays
Q65Question 65 of 109

Challenge: Shortest Subarray with Sum at Least K (Prefix Sums with a Monotonic Deque)

Key Concepts
  • The simple sliding window fails with negatives because shrinking can both increase and decrease the sum.
  • Prefix sums reduce the problem to finding the shortest span where prefix[i] - prefix[j] >= k.
  • A monotonic deque of prefix-sum indices stays strictly increasing from front to back.
  • Front pop: when prefix[i] - prefix[front] >= k, record the length and pop (no future right can give shorter).
  • Back pop: when prefix[back] >= prefix[i], pop (a higher earlier prefix is dominated by a lower later one).
  • Each index is pushed and popped at most once, giving O(N) total time.

Definition

Use prefix sums with a deque that stays increasing; pop the front when the window qualifies, pop the back when a newer prefix is not larger.
  • This problem finds the shortest subarray with sum >= k in an array with possible negatives.
  • The simple positive-only window fails because negatives break monotonicity.
  • Prefix sums turn subarray sums into prefix differences: P[i] - P[j] >= k.
  • A monotonic deque of indices keeps prefix values strictly increasing front to back.
  • Front pop: qualified left endpoints are never needed again for shorter subarrays.
  • Back pop: a higher earlier prefix is dominated by a lower later one as a left endpoint.
  • Time is O(N) and space is O(N).

At the National Bank of India, Rajesh reviews intraday net cash movements that can be positive or negative and needs the fewest consecutive transactions whose total reaches at least a liquidity target. Because a negative transaction can sink a running total, the simple trimming trick fails. He keeps an ordered list of candidate starting points, discards any that are worse than a newer one, and confirms starting points from the front as soon as the stretch qualifies.

Understanding the Concepts

What is the Shortest Subarray with Sum at Least K problem?

Given an array of integers that may include negatives and a target k, find the length of the shortest contiguous non-empty subarray whose sum is at least k. Return -1 if none exists. This is LeetCode 862.

For example, given [2, -1, 2] and k = 3, the answer is 3 (the entire array sums to 3).

Why does the simple sliding window fail?

With positive-only arrays, shrinking from the left always decreases the sum, so the greedy window works. With negatives, shrinking can increase the sum (removing a negative element), which means you might miss a shorter qualifying subarray by shrinking too early or not enough.

How do prefix sums help?

Let P be the prefix-sum array where P[0] = 0 and P[i] = sum of first i elements. The sum of the subarray from index j to i-1 is P[i] - P[j]. We want the shortest span i - j such that P[i] - P[j] >= k.

How does the monotonic deque work?

JAVA
package com.bank.service;

import java.util.ArrayDeque;
import java.util.Deque;

public class ShortestSubarrayAtLeastKService {

    /**
     * Finds the shortest transaction run reaching the liquidity target.
     * Handles negative flows using prefix sums + monotonic deque.
     * Used by the National Bank of India intraday-liquidity desk.
     */
    public static int shortestSubarray(int[] flows, int k) {
        int n = flows.length;
        long[] prefix = new long[n + 1];
        for (int i = 0; i < n; i++) {
            prefix[i + 1] = prefix[i] + flows[i];
        }

        Deque<Integer> deque = new ArrayDeque<>();
        int best = Integer.MAX_VALUE;

        for (int i = 0; i <= n; i++) {
            // Front pop: qualified left endpoints
            while (!deque.isEmpty()
                    && prefix[i] - prefix[deque.peekFirst()] >= k) {
                best = Math.min(best, i - deque.pollFirst());
            }
            // Back pop: dominated left endpoints
            while (!deque.isEmpty()
                    && prefix[deque.peekLast()] >= prefix[i]) {
                deque.pollLast();
            }
            deque.addLast(i);
        }
        return best == Integer.MAX_VALUE ? -1 : best;
    }

    public static void main(String[] args) {
        int[] flows = {20000, -10000, 30000, -5000, 15000};
        int target = 40000;
        int len = shortestSubarray(flows, target);
        System.out.println("Shortest run reaching Rs." + target + ": "
            + (len == -1 ? "impossible" : len + " transactions"));
    }
}
BASH
javac -d out com/bank/service/ShortestSubarrayAtLeastKService.java
java -cp out com.bank.service.ShortestSubarrayAtLeastKService
TEXT
Shortest run reaching Rs.40000: 4 transactions

Why is the front pop valid?

When prefix[i] - prefix[front] >= k, the span from front to i qualifies. Any future i' > i would give a longer span with the same front, so this left endpoint can never produce a shorter answer. We record the length and pop it permanently.

Why is the back pop valid?

If prefix[back] >= prefix[i], then the back index is a worse left endpoint than i for any future right endpoint: it is earlier (giving a longer span) and has a higher or equal prefix (giving a smaller difference). So it is strictly dominated and can be removed.

Why is this O(N)?

Each index is pushed onto the deque exactly once and popped at most once (either from the front or the back). So the total deque operations are at most 2N, and the prefix-sum computation is N. Total time is O(N).

What are the edge cases?

Several edge cases require attention.

  • No qualifying subarray: return -1 when even the total sum is less than k.
  • Single element >= k: the answer is 1.
  • All negatives: impossible to reach a positive k, return -1.
  • k = 0: every non-empty subarray with non-negative sum qualifies; the answer is 1 if any non-negative element exists.
JAVA
package com.bank.core;

public class ShortestSubarrayEdgeCases {
    public static void main(String[] args) {
        System.out.println(shortest(new int[]{1, 2, 3}, 100));   // -1
        System.out.println(shortest(new int[]{50000}, 50000));    // 1
        System.out.println(shortest(new int[]{-1, -2, -3}, 1));  // -1
    }

    static int shortest(int[] arr, int k) {
        int n = arr.length; long[] p = new long[n + 1];
        for (int i = 0; i < n; i++) p[i + 1] = p[i] + arr[i];
        java.util.Deque<Integer> dq = new java.util.ArrayDeque<>();
        int best = Integer.MAX_VALUE;
        for (int i = 0; i <= n; i++) {
            while (!dq.isEmpty() && p[i] - p[dq.peekFirst()] >= k) best = Math.min(best, i - dq.pollFirst());
            while (!dq.isEmpty() && p[dq.peekLast()] >= p[i]) dq.pollLast();
            dq.addLast(i);
        }
        return best == Integer.MAX_VALUE ? -1 : best;
    }
}
TEXT
-1
1
-1

What is the time and space complexity?

Time is O(N): prefix computation is O(N) and each deque operation is amortized O(1). Space is O(N) for the prefix array and the deque.

How does this compare to the positive-only variant?

| Variant | Array type | Algorithm | Time | Space |
|---|---|---|---|---|
| Positive only | All positive | Sliding window | O(N) | O(1) |
| With negatives | Mixed signs | Prefix + deque | O(N) | O(N) |

Recognizing when the simple window fails and reaching for prefix sums plus a monotonic deque is the real signal of depth.

What should a senior engineer know?

A senior engineer should understand these deeper aspects.

  • The dual pop is the core invariant: front pops handle qualification, back pops handle domination. Being able to articulate both is what proves understanding.
  • Use long for prefix sums: monetary data can overflow int across many transactions.
  • The positive-only window is a special case: when all elements are positive, the deque degenerates into a simple two-pointer because prefix sums are already monotonically increasing.
  • Related problems: sliding window maximum/minimum also use monotonic deques but for different invariants.

Visual Overview

DIAGRAM
  Flows: [20, -10, 30, -5, 15] (thousands), k=40
  Prefix: [0, 20, 10, 40, 35, 50]

  ┌──────────────────────────────────────────────┐
  │  i=0: P[0]=0, deque=[0]                      │
  │  i=1: P[1]=20, front: 20-0=20 < 40           │
  │        back: 0 < 20, keep. deque=[0,1]        │
  │  i=2: P[2]=10, front: 10-0=10 < 40           │
  │        back: P[1]=20 >= 10, pop 1. deque=[0,2]│
  │  i=3: P[3]=40, front: 40-0=40 >= 40!         │
  │        best=3-0=3, pop 0. deque=[2]           │
  │        front: 40-10=30 < 40                   │
  │        back: P[2]=10 < 40, keep. deque=[2,3]  │
  │  i=4: P[4]=35, front: 35-10=25 < 40          │
  │        back: P[3]=40 >= 35, pop 3. deque=[2,4]│
  │  i=5: P[5]=50, front: 50-10=40 >= 40!        │
  │        best=min(3, 5-2)=3, pop 2. deque=[4]   │
  │        front: 50-35=15 < 40                   │
  │        deque=[4,5]                            │
  └──────────────────────────────────────────────┘

  Result: shortest = 3 transactions

  Why simple window fails with negatives:
  ┌────────────────────────────────────────┐
  │  Positive: sum grows -> shrink left    │
  │  Negative: sum unpredictable -> broken │
  │  Solution: prefix sums + monotonic dq  │
  └────────────────────────────────────────┘

Key Takeaway

This problem asks for the shortest subarray with sum at least k in an array with possible negatives, and because negatives break the simple sliding window, you solve it with prefix sums and a monotonic deque in linear time. Prefix sums turn subarray sums into prefix differences, and for each right endpoint you want the latest left endpoint whose prefix is at most the current prefix minus k.

The deque maintains indices with strictly increasing prefix values from front to back. Two pops keep it correct: from the front, when the current prefix minus the front prefix is at least k, you record the length and pop (no future right can give shorter); from the back, when the back prefix is at least the current prefix, you pop it (a higher earlier prefix is dominated by a lower later one). Each index is pushed and popped at most once, so the total work is linear.

At the National Bank of India, Rajesh used this on the intraday-liquidity desk. Net cash movements could be positive or negative, and the team needed the shortest continuous run reaching a required liquidity target to raise the smallest possible block of funds quickly. The prefix-sum-with-monotonic-deque method gave the correct shortest run in linear time.

The senior-level insight is recognizing when the simple window fails (the moment negatives appear) and reaching for prefix sums plus a monotonic deque. Being able to justify both pops is the proof of understanding: front pops because a satisfied endpoint is never needed again, and back pops because a higher earlier prefix is strictly dominated.

Remember one sentence: prefix sums plus a monotonic deque with front pops for qualification and back pops for domination finds the shortest subarray with sum at least k in O(N), even with negatives.

---

Share
Use ← → arrow keys to navigate