Solving tips
- Because all elements are positive the window sum is monotone, enabling a shrinking window: grow right adding to a running sum, then shrink left while sum >= target.
- This is a minimum-length problem, so record right-left+1 INSIDE the shrink loop while the window is still valid, not after.
- Target O(n) time and O(1) space; return 0 (converting the n+1/inf sentinel) when no subarray qualifies.
- Common pitfall: use >= not > (to catch exact target hits), and note the template breaks with negative numbers, which need prefix sums plus binary search or a deque.
Problem
Given an array nums of positive integers and a positive integer target, return the length of the shortest contiguous subarray whose sum is greater than or equal to target. If no subarray reaches target, return 0.
Two details matter: elements are strictly positive (this is what makes the fast solution work), and you want the minimum length β a reversal of the more common βlongest valid windowβ setup.
Examples
target = 7, nums = [2, 3, 1, 2, 4, 3] β 2 β [4, 3] sums to 7; no single element reaches 7.
target = 4, nums = [1, 4, 4] β 1 β a single 4 already meets the target.
target = 11, nums = [1, 1, 1, 1, 1] β 0 β the whole array sums to 5, so no valid subarray exists.
Constraints
1 <= target <= 10^9
1 <= len(nums) <= 10^5
1 <= nums[i] <= 10^4
O(nΒ²) subarray enumeration is ~5 * 10^9 steps at n = 10^5. The expected solution is O(n); an O(n log n) prefix-sum + binary-search solution is a classic follow-up.
Think about it first
Hint 1
Because every element is positive, growing a window strictly increases its sum and shrinking strictly decreases it. Which direction do you move when the sum is too small? When it's big enough?
Hint 2
You want the shortest window meeting the target β so once the window's sum reaches target, greedily shrink it from the left while it still qualifies, recording each qualifying length.
Hint 3
One pass: extend right, adding to a running sum; then while sum >= target, record right - left + 1 and subtract nums[left], advancing left. Each pointer moves at most n times.
TL;DR
Shrinking sliding window (grow until sum β₯ target, then shrink while it stays β₯) β O(n) time, O(1) space.
Approach 1 β Brute force
For each start index, extend until the sum first reaches target (going further only lengthens the subarray).
class Solution:
def minSubArrayLen(self, target: int, nums: list[int]) -> int:
n = len(nums)
best = n + 1
for i in range(n):
total = 0
for j in range(i, n):
total += nums[j]
if total >= target:
best = min(best, j - i + 1)
break
return best if best <= n else 0
Complexity: O(nΒ²) time, O(1) space. At n = 10^5, worst case ~5 * 10^9 additions β the constraints forbid it.
Approach 2 β Sliding window
The insight: with all-positive elements the window sum is strictly monotone in both directions β extending increases it, shrinking decreases it. So as right advances, the shortest qualifying window ending at right starts at a left that never moves backward: grow until the sum qualifies, then shrink from the left while it still qualifies, recording every qualifying length.
class Solution:
def minSubArrayLen(self, target: int, nums: list[int]) -> int:
n = len(nums)
best = n + 1
total = 0
left = 0
for right, x in enumerate(nums):
total += x
while total >= target: # qualifies: record, then try shorter
best = min(best, right - left + 1)
total -= nums[left]
left += 1
return best if best <= n else 0
Walkthrough on target = 7, nums = [2, 3, 1, 2, 4, 3]:
| right | x | total (after add) | shrink steps (record β drop) | left after | best |
|---|
| 0 | 2 | 2 | β | 0 | β |
| 1 | 3 | 5 | β | 0 | β |
| 2 | 1 | 6 | β | 0 | β |
| 3 | 2 | 8 | record len 4 β drop 2 (total 6) | 1 | 4 |
| 4 | 4 | 10 | record len 4 β drop 3 (total 7); record len 3 β drop 1 (total 6) | 3 | 3 |
| 5 | 3 | 9 | record len 3 β drop 2 (total 7); record len 2 β drop 4 (total 3) | 5 | 2 |
Answer: 2 ([4, 3]).
Complexity: left and right each advance at most n times, so O(n) time despite the nested loop; O(1) space.
Approach 3 β Prefix sums + binary search (the follow-up)
The insight: positive elements make the prefix-sum array strictly increasing, so for each start i the smallest end with prefix[j] - prefix[i] >= target can be found by binary search instead of a scan. Worth knowing because it survives even when a problem forces per-start queries.
import bisect
from itertools import accumulate
class Solution:
def minSubArrayLen(self, target: int, nums: list[int]) -> int:
n = len(nums)
prefix = [0] + list(accumulate(nums)) # prefix[j] = sum of nums[:j], strictly increasing
best = n + 1
for i in range(n):
j = bisect.bisect_left(prefix, prefix[i] + target, i + 1)
if j <= n:
best = min(best, j - i)
return best if best <= n else 0
Complexity: O(n log n) time, O(n) space. Strictly worse than the window here β mention it as the follow-up, reach for it when windows donβt apply.
Common pitfalls
- Count-before-shrink, not shrink-before-count. This is a minimum-length problem: record the length while the window is valid, i.e. inside the shrink loop before dropping
nums[left]. Recording after the loop (the longest-window habit) measures an invalid, too-short window.
- Returning
n + 1 (or inf) instead of 0 when no window qualifies β remember the sentinel-to-zero conversion at the end.
- Assuming this works with negatives. It doesnβt: a negative element breaks the monotone sum, so shrinking might increase the sum and the frontier argument collapses (that variant needs prefix sums with a monotone deque).
>= vs >: the target is met at greater than or equal; using > silently misses exact hits like target = 7, window [4, 3].
Pattern takeaway
The mirror image of βlongest valid windowβ: for shortest qualifying window, grow until you qualify, then shrink greedily while you still qualify β and record inside the shrink phase, since there validity holds. The license for all of it is monotonicity of the window aggregate, which here comes from the all-positive guarantee; always check that guarantee before reaching for this template.