Find the k-th smallest sum among all contiguous subarrays, usually for nonnegative arrays.
Kth Smallest Subarray Sum finds the k-th smallest value among all contiguous subarray sums.
For nonnegative arrays, subarray sums have a monotone structure. This allows binary search on the answer and a sliding window count for how many subarrays have sum at most a candidate value.
Problem
Given an array of length with nonnegative values and a 1-based rank , find the k-th smallest sum among all subarrays:
where:
There are:
nonempty subarrays.
Algorithm
Binary search over possible sum values. For each candidate sum , count how many subarrays have sum at most .
kth_smallest_subarray_sum(A, k):
low = min(A)
high = sum(A)
while low < high:
mid = floor((low + high) / 2)
if count_subarrays_at_most(A, mid) < k:
low = mid + 1
else:
high = mid
return lowFor nonnegative arrays, the counting step uses a sliding window.
count_subarrays_at_most(A, s):
count = 0
left = 0
current = 0
for right from 0 to n - 1:
current = current + A[right]
while current > s:
current = current - A[left]
left = left + 1
count = count + (right - left + 1)
return countExample
Let:
All subarray sums are:
Sorted:
For , the answer is:
Correctness
For a fixed threshold , count_subarrays_at_most counts all subarrays whose sum is at most . Because all values are nonnegative, increasing the right endpoint can only increase the current window sum, and moving the left endpoint can only decrease it.
After the while loop, every subarray ending at right and starting from any index between left and right has sum at most . There are exactly right - left + 1 such subarrays.
The predicate “at least subarray sums are at most ” is monotone. Therefore binary search returns the smallest sum satisfying it, which is the k-th smallest subarray sum.
Complexity
| step | cost |
|---|---|
| one counting pass | |
| binary search over sum range | |
| total | |
| space |
where:
When to Use
Use this method when the array contains nonnegative values and generating all subarray sums would be too expensive.
If negative numbers are allowed, the sliding window count no longer works. In that case, use prefix sums with an ordered structure or a divide and conquer counting method.
Implementation
def count_subarrays_at_most(a, limit):
count = 0
left = 0
current = 0
for right, x in enumerate(a):
current += x
while current > limit:
current -= a[left]
left += 1
count += right - left + 1
return count
def kth_smallest_subarray_sum(a, k):
low = min(a)
high = sum(a)
while low < high:
mid = (low + high) // 2
if count_subarrays_at_most(a, mid) < k:
low = mid + 1
else:
high = mid
return lowfunc countSubarraysAtMost(a []int, limit int) int {
count := 0
left := 0
current := 0
for right, x := range a {
current += x
for current > limit {
current -= a[left]
left++
}
count += right - left + 1
}
return count
}
func KthSmallestSubarraySum(a []int, k int) int {
low := a[0]
high := 0
for _, x := range a {
if x < low {
low = x
}
high += x
}
for low < high {
mid := low + (high-low)/2
if countSubarraysAtMost(a, mid) < k {
low = mid + 1
} else {
high = mid
}
}
return low
}