LeetCode 3364 - Minimum Positive Sum Subarray
The problem asks us to find the smallest positive sum among all subarrays whose lengths fall within a given range [l, r]. A subarray is a contiguous section of the array. We are allowed to choose any non-empty contiguous segment as long as: 1.
Difficulty: 🟢 Easy
Topics: Array, Sliding Window, Prefix Sum
Solution
Problem Understanding
The problem asks us to find the smallest positive sum among all subarrays whose lengths fall within a given range [l, r].
A subarray is a contiguous section of the array. We are allowed to choose any non-empty contiguous segment as long as:
- Its length is between
landr, inclusive. - Its sum is strictly greater than
0.
Among all such valid subarrays, we must return the minimum positive sum. If no valid subarray exists, we return -1.
The input consists of:
nums, an integer arrayl, the minimum allowed subarray lengthr, the maximum allowed subarray length
The output is a single integer representing the smallest positive subarray sum that satisfies the length restriction.
The constraints are relatively small:
nums.length <= 100- Each value is between
-1000and1000
These limits are important because they tell us that even an O(n^2) or O(n^3) solution could potentially pass. However, we still want to design a clean and efficient solution.
Several edge cases are important:
- Every valid subarray may have a non-positive sum, in which case we return
-1. - The array may contain all negative numbers.
- The minimum positive sum could come from a larger subarray, not necessarily the shortest one.
- Multiple subarrays may produce the same minimum positive sum.
- The range
[l, r]may span the entire array. - Single-element subarrays are possible when
l = 1.
Because the problem asks for the minimum positive sum, we must carefully ignore sums that are 0 or negative.
Approaches
Brute Force Approach
The most direct solution is to generate every possible subarray whose length lies between l and r.
For each starting index:
- Try every allowed subarray length.
- Compute the subarray sum.
- If the sum is positive, update the answer with the minimum value seen so far.
A naive implementation would recompute every subarray sum from scratch. Since each sum computation may take O(n) time, the total complexity becomes O(n^3).
This works because we explicitly examine every valid subarray, guaranteeing that we never miss the optimal answer. However, repeatedly summing overlapping ranges is inefficient.
Improved Approach Using Prefix Sums
The key observation is that subarray sums can be computed efficiently using prefix sums.
If:
$$\text{prefix}[i]$$
stores the sum of the first i elements, then the sum of subarray:
$$nums[left:right]$$
can be computed in constant time as:
$$\text{prefix}[right] - \text{prefix}[left]$$
This avoids recomputing sums repeatedly.
We still enumerate all valid subarrays, but each sum calculation becomes O(1) instead of O(n).
Since the array size is only 100, this O(n^2) solution is both simple and efficient.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n³) | O(1) | Recomputes each subarray sum repeatedly |
| Optimal | O(n²) | O(n) | Uses prefix sums for constant-time range sum queries |
Algorithm Walkthrough
Optimal Prefix Sum Algorithm
- Create a prefix sum array of size
n + 1.
The prefix sum at index i stores the total sum of the first i elements. This allows fast subarray sum computation.
2. Initialize the answer variable.
We can initialize it to infinity so that any positive sum found becomes the current best answer. 3. Iterate through every possible starting index.
For each start, we will try all valid subarray lengths between l and r.
4. Compute the ending index.
For a chosen length length, the ending position becomes:
$$end = start + length$$
If end exceeds the array size, we stop checking longer lengths.
5. Compute the subarray sum using prefix sums.
The sum of the subarray from start to end - 1 is:
$\text{sum} = \text{prefix}[end] - \text{prefix}[start]$
This computation takes constant time. 6. Check whether the sum is positive.
If the sum is greater than 0, update the answer with the minimum value seen so far.
7. After examining all valid subarrays, return the result.
If the answer was never updated, return -1.
Why it works
The algorithm systematically checks every subarray whose length lies in [l, r]. Prefix sums guarantee that every subarray sum is computed correctly in constant time. Since we only update the answer when a smaller positive sum is found, the final answer is guaranteed to be the minimum positive subarray sum among all valid candidates.
Python Solution
from typing import List
class Solution:
def minimumSumSubarray(self, nums: List[int], l: int, r: int) -> int:
n = len(nums)
# Build prefix sum array
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + nums[i]
minimum_positive_sum = float("inf")
# Try every starting index
for start in range(n):
# Try every allowed subarray length
for length in range(l, r + 1):
end = start + length
if end > n:
break
current_sum = prefix[end] - prefix[start]
if current_sum > 0:
minimum_positive_sum = min(
minimum_positive_sum,
current_sum
)
return minimum_positive_sum if minimum_positive_sum != float("inf") else -1
The implementation begins by constructing a prefix sum array. Each entry stores the cumulative sum up to that point in the array.
Next, we iterate through every possible starting index. For each starting position, we try every valid subarray length from l to r.
The prefix sum array allows us to compute each subarray sum in constant time instead of summing elements manually. Whenever we encounter a positive sum, we compare it with the current best answer and keep the smaller value.
At the end, if no positive subarray sum was found, the answer remains infinity, so we return -1.
Go Solution
package main
import "math"
func minimumSumSubarray(nums []int, l int, r int) int {
n := len(nums)
// Build prefix sum array
prefix := make([]int, n+1)
for i := 0; i < n; i++ {
prefix[i+1] = prefix[i] + nums[i]
}
minimumPositiveSum := math.MaxInt
// Try every starting index
for start := 0; start < n; start++ {
// Try every allowed length
for length := l; length <= r; length++ {
end := start + length
if end > n {
break
}
currentSum := prefix[end] - prefix[start]
if currentSum > 0 && currentSum < minimumPositiveSum {
minimumPositiveSum = currentSum
}
}
}
if minimumPositiveSum == math.MaxInt {
return -1
}
return minimumPositiveSum
}
The Go implementation follows the same logic as the Python version.
One small difference is that Go does not have a built-in infinity value for integers, so we use math.MaxInt as the initial sentinel value.
Go slices are used for the prefix sum array, and all computations remain integer-safe because the constraints are small enough that overflow is not a concern.
Worked Examples
Example 1
Input:
nums = [3, -2, 1, 4]
l = 2
r = 3
Prefix sum array:
| Index | Prefix Sum |
|---|---|
| 0 | 0 |
| 1 | 3 |
| 2 | 1 |
| 3 | 2 |
| 4 | 6 |
Now examine all valid subarrays.
| Start | Length | End | Subarray | Sum | Valid? |
|---|---|---|---|---|---|
| 0 | 2 | 2 | [3, -2] | 1 | Yes |
| 0 | 3 | 3 | [3, -2, 1] | 2 | Yes |
| 1 | 2 | 3 | [-2, 1] | -1 | No |
| 1 | 3 | 4 | [-2, 1, 4] | 3 | Yes |
| 2 | 2 | 4 | [1, 4] | 5 | Yes |
The smallest positive sum is 1.
Output:
1
Example 2
Input:
nums = [-2, 2, -3, 1]
l = 2
r = 3
Prefix sums:
| Index | Prefix Sum |
|---|---|
| 0 | 0 |
| 1 | -2 |
| 2 | 0 |
| 3 | -3 |
| 4 | -2 |
Valid checks:
| Subarray | Sum |
|---|---|
| [-2, 2] | 0 |
| [-2, 2, -3] | -3 |
| [2, -3] | -1 |
| [2, -3, 1] | 0 |
| [-3, 1] | -2 |
No positive sums exist.
Output:
-1
Example 3
Input:
nums = [1, 2, 3, 4]
l = 2
r = 4
Possible valid sums:
| Subarray | Sum |
|---|---|
| [1, 2] | 3 |
| [2, 3] | 5 |
| [3, 4] | 7 |
| [1, 2, 3] | 6 |
| [2, 3, 4] | 9 |
| [1, 2, 3, 4] | 10 |
The minimum positive sum is 3.
Output:
3
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n²) | We examine all valid subarrays |
| Space | O(n) | Prefix sum array requires linear extra space |
The outer loop iterates over all starting indices, and the inner loop iterates over all allowed lengths. Since the maximum array size is only 100, this approach is very efficient in practice.
The prefix sum array stores cumulative sums for fast range queries, which requires O(n) additional memory.
Test Cases
from typing import List
class Solution:
def minimumSumSubarray(self, nums: List[int], l: int, r: int) -> int:
n = len(nums)
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + nums[i]
answer = float("inf")
for start in range(n):
for length in range(l, r + 1):
end = start + length
if end > n:
break
current_sum = prefix[end] - prefix[start]
if current_sum > 0:
answer = min(answer, current_sum)
return answer if answer != float("inf") else -1
sol = Solution()
assert sol.minimumSumSubarray([3, -2, 1, 4], 2, 3) == 1
# Example 1
assert sol.minimumSumSubarray([-2, 2, -3, 1], 2, 3) == -1
# Example 2
assert sol.minimumSumSubarray([1, 2, 3, 4], 2, 4) == 3
# Example 3
assert sol.minimumSumSubarray([5], 1, 1) == 5
# Single positive element
assert sol.minimumSumSubarray([-5], 1, 1) == -1
# Single negative element
assert sol.minimumSumSubarray([0], 1, 1) == -1
# Zero is not positive
assert sol.minimumSumSubarray([1, -1, 1, -1], 1, 2) == 1
# Minimum positive single-element subarray
assert sol.minimumSumSubarray([-1, -2, -3], 1, 3) == -1
# All negative numbers
assert sol.minimumSumSubarray([2, 2, 2], 2, 3) == 4
# Multiple valid subarrays
assert sol.minimumSumSubarray([10, -9, 1], 2, 2) == 1
# Smallest positive sum appears late
assert sol.minimumSumSubarray([100, -99, 50], 2, 3) == 1
# Prefix sum handling
assert sol.minimumSumSubarray([1, 1, 1, 1], 4, 4) == 4
# Entire array only
| Test | Why |
|---|---|
[3, -2, 1, 4], l=2, r=3 |
Validates standard mixed-number scenario |
[-2, 2, -3, 1], l=2, r=3 |
Ensures -1 is returned when no positive sum exists |
[1, 2, 3, 4], l=2, r=4 |
Checks normal positive-only array |
[5], l=1, r=1 |
Smallest valid input |
[-5], l=1, r=1 |
Single negative element |
[0], l=1, r=1 |
Zero should not count as positive |
[1, -1, 1, -1], l=1, r=2 |
Mixed positive and negative values |
[-1, -2, -3], l=1, r=3 |
All negative array |
[2, 2, 2], l=2, r=3 |
Multiple overlapping valid subarrays |
[10, -9, 1], l=2, r=2 |
Minimum positive sum occurs later |
[100, -99, 50], l=2, r=3 |
Verifies prefix sum correctness |
[1, 1, 1, 1], l=4, r=4 |
Only full-array subarray allowed |
Edge Cases
One important edge case occurs when no positive subarray sum exists. This can happen when the array contains only negative numbers or when every valid subarray sums to zero or less. A buggy implementation might accidentally return 0 or an uninitialized value. This implementation avoids that problem by initializing the answer to infinity and only updating it for strictly positive sums.
Another important case is when l = r = 1. In this situation, only single-element subarrays are allowed. The implementation still works naturally because the nested loops examine subarrays of exactly one element, and the prefix sum computation remains valid.
A third edge case involves subarrays near the end of the array. If the chosen length causes the subarray to extend beyond the array boundary, accessing invalid indices could cause errors. The implementation handles this safely with:
if end > n:
break
This ensures that only valid subarrays are processed.
A final subtle case occurs when the minimum positive sum is very small and surrounded by larger sums. For example:
[100, -99, 50]
The correct answer is 1, from the subarray [100, -99]. Since the algorithm checks every valid subarray and always keeps the smallest positive sum seen so far, it correctly identifies this optimal value.