Builds on Longest Window, which you haven’t finished yet.
Exactly K = At Most K − At Most (K−1)
You get an array nums in which every element is 0 or 1, and a whole number goal. A subarray is a run of one or more neighbouring elements of nums, taken without gaps. Count the subarrays whose elements add up to exactly goal, and return that number.
Subarrays are told apart by where they start and end: two runs at different positions both count, even if they hold the same values. When goal is 0, the subarrays made only of zeros are the ones that count.
1,0,1), 0 to 3 (1,0,1,0), 1 to 4 (0,1,0,1) and 2 to 4 (1,0,1).At-Most Trick
exactly K âž” atMost(K) - atMost(K - 1) âž” two shrinking windows| 1 | from typing import Callable |
| 2 | Â |
| 3 | Â |
| 4 | def exactly(at_most: Callable[[int], int], k: int) -> int: |
| 5 | """At-Most Trick: how many items measure exactly k, counted as at_most(k) - at_most(k - 1). |
| 6 | at_most(limit) counts the items that measure at most limit, for example the subarrays with at most limit distinct values. |
| 7 | Precondition: every measure is a whole number, so an item that measures at most k measures exactly k or at most k - 1, never both. |
| 8 | at_most(-1) must return 0. |
| 9 | A range such as Number of Subarrays with Bounded Maximum is at_most(right) - at_most(left - 1), outside this template. |
| 10 | T = the time of one at_most call. S = the space of one at_most call. |
| 11 | Time: O(T) | Space: O(S) |
| 12 | """ |
| 13 | return at_most(k) - at_most(k - 1) # At most k minus at most k - 1 leaves exactly k |
right, nums[left..right] is the longest window ending at right whose sum is at most limit, so count holds how many subarrays with sum at most limit end at or before right."Exactly goal" can't be kept by one window: zeros can join or leave without changing the sum, so there is no single left edge to move, but "at most limit" can, and at_most(limit) adds right - left + 1 for every right (each start from left to right fits), so numSubarraysWithSum returns at_most(goal) - at_most(goal - 1). The subtraction is exact because a subarray with sum at most goal has sum goal or sum at most goal - 1, never both; and the window is right because the values are never negative: if nums[left..right] fits, every shorter window ending at right fits too, so the while window > limit loop only ever moves left forward. The trap is goal = 0: at_most(goal - 1) becomes at_most(-1), which must return 0 (if limit < 0: return 0), or the while loop never finds a sum small enough and walks left past right and off the end of nums.
See it run
Binary Subarrays With Sum: at_most(2) - at_most(1), nums = [1, 0, 1, 0, 1]
limit 2window 0count 0
The top row is nums. The bottom row shows what each right added to count. The board runs at_most twice, and the second run starts again from the left.
leftthe first index of the windowrightthe index that was just added to the windowleft, rightleft and right are at the same indexwindowan index between left and rightaddedthe amount that this right added to count
Call at_most(2). Here left, window and count all start at 0.
Gotcha 1 of 4
- The trap
Gotcha 1: Leaving out
if limit < 0: return 0Mistakepass # no guardFixif limit < 0: return 0What happens: With
goal = 0, the second call isat_most(-1). No window has a sum below 0, sowhile window > limitis always True. The loop movesleftpastrightand off the end ofnums. Python then raises an IndexError.Example: nums = [0, 0, 0, 0, 0], goal = 0Mistake: raises IndexErrorFix: returns 15
- The question asks for the longest window within "at most K", as in Longest Substring with At Most K Distinct Characters (LC 340): one window answers it, with no count of every subarray and no subtraction. See Longest Window
- Values can be negative, as in Subarray Sum Equals K (LC 560): adding an element can lower the sum, so "sum at most K" is not a window that only shrinks from the left. See Prefix Sum Map
Deep Dive
Why a single window cannot count an exact sum
The Longest Window card moves left forward only when the window breaks a rule, and a longer window never becomes valid again after it breaks. A rule of the form "sum at most limit" has this property when no value is negative. Adding nums[right] can only raise window, and dropping nums[left] can only lower it.
A rule of the form "sum exactly goal" has no such property. Take nums = [1, 0, 1, 0, 1] and goal = 2. The window [1, 0, 1] has sum 2. Add the next 0, and the sum is still 2, so the window stays valid. Drop the first 1 from [1, 0, 1, 0], and the sum falls to 1, so it becomes invalid. Zeros can join or leave without changing the sum. So there is no single left that you can move forward, and no way to say that a window is the longest one.
The fix is to count a different set. at_most(limit) counts the subarrays whose sum is at most limit. That count does have a window: when window > limit, move left forward until the sum fits.
Tracing at_most(2) - at_most(1) on [1, 0, 1, 0, 1]
at_most(limit) adds right - left + 1 to count at every right. The window nums[left..right] is the longest window ending at right with a sum at most limit. Every shorter window that ends at right has a sum that is not larger, so it fits too. The starts left, left + 1, up to right give exactly right - left + 1 subarrays.
With limit = 2, the added amounts for right = 0 to 4 are 1, 2, 3, 4 and 4, so at_most(2) returns 14. At right = 4, the window sum is 3, so one drop of nums[0] = 1 brings it back to 2, and left becomes 1.
With limit = 1, the added amounts are 1, 2, 2, 3 and 2, so at_most(1) returns 10. At right = 4, the sum is 2. Dropping nums[1] = 0 leaves the sum at 2, so the loop must drop again. This is why the shrink step is a while. An if would count a window whose sum is above limit.
The answer is 14 - 10 = 4. This matches Binary Subarrays With Sum (LC 930), Example 1.
Why the subtraction is exact, and the goal = 0 trap
Every subarray with sum at most goal has a sum that is goal, or a sum that is at most goal - 1. It cannot be both. So at_most(goal) counts the exact-goal subarrays plus the at-most-goal - 1 subarrays, and at_most(goal - 1) counts only the second group. The difference leaves the exact-goal subarrays.
This needs whole-number measures that never fall when an element is added. A sum of values that are never negative works. A count of different values works too, because adding an element can only keep that count or raise it. A sum that includes negative values does not work. There, dropping nums[left] can raise the sum, so the while loop is not a valid rule. Subarray Sum Equals K (LC 560) needs a prefix-sum lookup instead.
The trap is goal = 0. The second call becomes at_most(-1). No subarray of 0s and 1s has a negative sum, so window > limit is always true. The while loop keeps moving left past right and off the end of nums. The guard if limit < 0: return 0 stops this. For nums = [0, 0, 0, 0, 0] and goal = 0, the correct answer is 15 (5 + 4 + 3 + 2 + 1), and the unguarded code raises IndexError.
Using the template on other problems, and the cost
The template exactly(at_most, k) = at_most(k) - at_most(k - 1) does not depend on the window. Only at_most changes.
For Subarrays with K Different Integers (LC 992), at_most(limit) keeps a count of each value in the window and shrinks while the number of different values is above limit. For Count Number of Nice Subarrays (LC 1248), map each number to x % 2 first. Then the sum of the window is the number of odd values, and the code is the same as for Binary Subarrays With Sum (LC 930).
Each at_most call moves right and left forward at most N times each, so one call costs O(N) time. Two calls cost O(N) time. The space is O(1) for the sum version. The distinct-values version stores a count for each value in the window, which costs O(N) space in the worst case.
A range such as Number of Subarrays with Bounded Maximum (LC 795) uses at_most(right) - at_most(left - 1) in a different form. The card's template covers exact values only.
60-Second Pitch
"One window cannot hold an exact sum, because zeros can join or leave without changing it. So I count something a window can hold: the subarrays with a sum at most limit. For each right, I add the new value and shrink from the left while the sum is above limit. Every start from left to right gives a valid subarray, so I add right - left + 1 to count. A subarray with a sum at most goal has a sum of exactly goal or at most goal - 1, never both. So the answer is at_most(goal) - at_most(goal - 1). I return 0 for a negative limit, or goal = 0 breaks the loop. Each call is O(N) time and O(1) space, so the whole solution is O(N) time and O(1) space."
Common Questions
- Why not keep one window and count only the windows with an exact sum?
Zeros can join or leave the window without changing its sum. So the window with sum
goalhas no single left edge. Each right end has a range of valid left ends, and one pointer cannot move through it in one pass. The at-most version has one left end for each right end, so it works.- What is the time and space complexity?
Each
at_mostcall movesrightonce overnumsandleftat most once overnums, so it isO(N)time. Two calls are stillO(N)time. The sum version needsO(1)space. The version for different values stores one count for each value in the window, which isO(N)space in the worst case.- Does this work when the array has negative numbers?
No. Dropping
nums[left]from the window can raise the sum, so "sum at mostlimit" is not a window that only shrinks. Use prefix sums and a hash map, as in Subarray Sum Equals K (LC 560).- How do you adapt it to count subarrays with exactly K different integers?
Replace the sum with a count of each value in the window. Shrink while the number of different values is above
limit. The answer for Subarrays with K Different Integers (LC 992) is againat_most(k) - at_most(k - 1). The measure never falls when an element is added, so the same argument holds.- Is there a one-pass version?
Yes, for some problems. A second pointer can track the start of the earliest window that still has exactly
goal, but the bookkeeping is easy to get wrong. The two-call version is shorter to write and easier to check, so it is the usual choice in an interview.
SWE Reasoning
Spot it
- The question counts subarrays with an exact value: exactly
goalas a sum, exactly K odd numbers, or exactly K different values. - Adding an element never lowers the measure, so "at most K" is a window that only shrinks from the left.
- A single window fails for "exactly K", because a zero, an even number or a repeated value changes nothing about the measure.
- The answer is a count of subarrays, not the length of the longest one.
The invariant, step by step
at_most(limit)returns 0 at once whenlimit < 0. No subarray has a measure below zero.- For each
right,window += nums[right]adds the new value to the sum ofnums[left..right]. - While
window > limit,window -= nums[left]andleft += 1drop the oldest value. The window ends as the longest one ending atrightwith a sum at mostlimit. count += right - left + 1counts every start fromlefttoright. Each of them gives a subarray ending atrightwith a sum at mostlimit.- After the last
right,countholds every subarray with a sum at mostlimit. at_most(goal) - at_most(goal - 1)removes the subarrays with a sum at mostgoal - 1from the subarrays with a sum at mostgoal. Only the subarrays with a sum of exactlygoalremain.
Traps
- Leaving out
if limit < 0: return 0. Withgoal = 0,at_most(-1)never finds a small enough sum, andleftruns off the end ofnums. - Adding 1 per step.
count += 1counts only the longest window at eachright. The answer for five zeros andgoal = 0is 5 instead of 15. - Shrinking with
ifinstead ofwhile. One drop may remove a zero, so the sum stays abovelimitand the window is counted while it is too large. - Using the template when values can be negative. The window rule needs a measure that never falls when an element is added.
- Forgetting
x % 2for odd numbers. Without it, the window adds up the numbers themselves, not the count of odd numbers.
Practice
Builds on Longest Window, which you haven’t finished yet.
Exactly K = At Most K − At Most (K−1)
You get an array nums in which every element is 0 or 1, and a whole number goal. A subarray is a run of one or more neighbouring elements of nums, taken without gaps. Count the subarrays whose elements add up to exactly goal, and return that number.
Subarrays are told apart by where they start and end: two runs at different positions both count, even if they hold the same values. When goal is 0, the subarrays made only of zeros are the ones that count.
1,0,1), 0 to 3 (1,0,1,0), 1 to 4 (0,1,0,1) and 2 to 4 (1,0,1).At-Most Trick
exactly K âž” atMost(K) - atMost(K - 1) âž” two shrinking windows| 1 | from typing import Callable |
| 2 | Â |
| 3 | Â |
| 4 | def exactly(at_most: Callable[[int], int], k: int) -> int: |
| 5 | """At-Most Trick: how many items measure exactly k, counted as at_most(k) - at_most(k - 1). |
| 6 | at_most(limit) counts the items that measure at most limit, for example the subarrays with at most limit distinct values. |
| 7 | Precondition: every measure is a whole number, so an item that measures at most k measures exactly k or at most k - 1, never both. |
| 8 | at_most(-1) must return 0. |
| 9 | A range such as Number of Subarrays with Bounded Maximum is at_most(right) - at_most(left - 1), outside this template. |
| 10 | T = the time of one at_most call. S = the space of one at_most call. |
| 11 | Time: O(T) | Space: O(S) |
| 12 | """ |
| 13 | return at_most(k) - at_most(k - 1) # At most k minus at most k - 1 leaves exactly k |
right, nums[left..right] is the longest window ending at right whose sum is at most limit, so count holds how many subarrays with sum at most limit end at or before right."Exactly goal" can't be kept by one window: zeros can join or leave without changing the sum, so there is no single left edge to move, but "at most limit" can, and at_most(limit) adds right - left + 1 for every right (each start from left to right fits), so numSubarraysWithSum returns at_most(goal) - at_most(goal - 1). The subtraction is exact because a subarray with sum at most goal has sum goal or sum at most goal - 1, never both; and the window is right because the values are never negative: if nums[left..right] fits, every shorter window ending at right fits too, so the while window > limit loop only ever moves left forward. The trap is goal = 0: at_most(goal - 1) becomes at_most(-1), which must return 0 (if limit < 0: return 0), or the while loop never finds a sum small enough and walks left past right and off the end of nums.
See it run
Binary Subarrays With Sum: at_most(2) - at_most(1), nums = [1, 0, 1, 0, 1]
limit 2window 0count 0
The top row is nums. The bottom row shows what each right added to count. The board runs at_most twice, and the second run starts again from the left.
leftthe first index of the windowrightthe index that was just added to the windowleft, rightleft and right are at the same indexwindowan index between left and rightaddedthe amount that this right added to count
Call at_most(2). Here left, window and count all start at 0.
Gotcha 1 of 4
- The trap
Gotcha 1: Leaving out
if limit < 0: return 0Mistakepass # no guardFixif limit < 0: return 0What happens: With
goal = 0, the second call isat_most(-1). No window has a sum below 0, sowhile window > limitis always True. The loop movesleftpastrightand off the end ofnums. Python then raises an IndexError.Example: nums = [0, 0, 0, 0, 0], goal = 0Mistake: raises IndexErrorFix: returns 15
- The question asks for the longest window within "at most K", as in Longest Substring with At Most K Distinct Characters (LC 340): one window answers it, with no count of every subarray and no subtraction. See Longest Window
- Values can be negative, as in Subarray Sum Equals K (LC 560): adding an element can lower the sum, so "sum at most K" is not a window that only shrinks from the left. See Prefix Sum Map
Deep Dive
Why a single window cannot count an exact sum
The Longest Window card moves left forward only when the window breaks a rule, and a longer window never becomes valid again after it breaks. A rule of the form "sum at most limit" has this property when no value is negative. Adding nums[right] can only raise window, and dropping nums[left] can only lower it.
A rule of the form "sum exactly goal" has no such property. Take nums = [1, 0, 1, 0, 1] and goal = 2. The window [1, 0, 1] has sum 2. Add the next 0, and the sum is still 2, so the window stays valid. Drop the first 1 from [1, 0, 1, 0], and the sum falls to 1, so it becomes invalid. Zeros can join or leave without changing the sum. So there is no single left that you can move forward, and no way to say that a window is the longest one.
The fix is to count a different set. at_most(limit) counts the subarrays whose sum is at most limit. That count does have a window: when window > limit, move left forward until the sum fits.
Tracing at_most(2) - at_most(1) on [1, 0, 1, 0, 1]
at_most(limit) adds right - left + 1 to count at every right. The window nums[left..right] is the longest window ending at right with a sum at most limit. Every shorter window that ends at right has a sum that is not larger, so it fits too. The starts left, left + 1, up to right give exactly right - left + 1 subarrays.
With limit = 2, the added amounts for right = 0 to 4 are 1, 2, 3, 4 and 4, so at_most(2) returns 14. At right = 4, the window sum is 3, so one drop of nums[0] = 1 brings it back to 2, and left becomes 1.
With limit = 1, the added amounts are 1, 2, 2, 3 and 2, so at_most(1) returns 10. At right = 4, the sum is 2. Dropping nums[1] = 0 leaves the sum at 2, so the loop must drop again. This is why the shrink step is a while. An if would count a window whose sum is above limit.
The answer is 14 - 10 = 4. This matches Binary Subarrays With Sum (LC 930), Example 1.
Why the subtraction is exact, and the goal = 0 trap
Every subarray with sum at most goal has a sum that is goal, or a sum that is at most goal - 1. It cannot be both. So at_most(goal) counts the exact-goal subarrays plus the at-most-goal - 1 subarrays, and at_most(goal - 1) counts only the second group. The difference leaves the exact-goal subarrays.
This needs whole-number measures that never fall when an element is added. A sum of values that are never negative works. A count of different values works too, because adding an element can only keep that count or raise it. A sum that includes negative values does not work. There, dropping nums[left] can raise the sum, so the while loop is not a valid rule. Subarray Sum Equals K (LC 560) needs a prefix-sum lookup instead.
The trap is goal = 0. The second call becomes at_most(-1). No subarray of 0s and 1s has a negative sum, so window > limit is always true. The while loop keeps moving left past right and off the end of nums. The guard if limit < 0: return 0 stops this. For nums = [0, 0, 0, 0, 0] and goal = 0, the correct answer is 15 (5 + 4 + 3 + 2 + 1), and the unguarded code raises IndexError.
Using the template on other problems, and the cost
The template exactly(at_most, k) = at_most(k) - at_most(k - 1) does not depend on the window. Only at_most changes.
For Subarrays with K Different Integers (LC 992), at_most(limit) keeps a count of each value in the window and shrinks while the number of different values is above limit. For Count Number of Nice Subarrays (LC 1248), map each number to x % 2 first. Then the sum of the window is the number of odd values, and the code is the same as for Binary Subarrays With Sum (LC 930).
Each at_most call moves right and left forward at most N times each, so one call costs O(N) time. Two calls cost O(N) time. The space is O(1) for the sum version. The distinct-values version stores a count for each value in the window, which costs O(N) space in the worst case.
A range such as Number of Subarrays with Bounded Maximum (LC 795) uses at_most(right) - at_most(left - 1) in a different form. The card's template covers exact values only.
60-Second Pitch
"One window cannot hold an exact sum, because zeros can join or leave without changing it. So I count something a window can hold: the subarrays with a sum at most limit. For each right, I add the new value and shrink from the left while the sum is above limit. Every start from left to right gives a valid subarray, so I add right - left + 1 to count. A subarray with a sum at most goal has a sum of exactly goal or at most goal - 1, never both. So the answer is at_most(goal) - at_most(goal - 1). I return 0 for a negative limit, or goal = 0 breaks the loop. Each call is O(N) time and O(1) space, so the whole solution is O(N) time and O(1) space."
Common Questions
- Why not keep one window and count only the windows with an exact sum?
Zeros can join or leave the window without changing its sum. So the window with sum
goalhas no single left edge. Each right end has a range of valid left ends, and one pointer cannot move through it in one pass. The at-most version has one left end for each right end, so it works.- What is the time and space complexity?
Each
at_mostcall movesrightonce overnumsandleftat most once overnums, so it isO(N)time. Two calls are stillO(N)time. The sum version needsO(1)space. The version for different values stores one count for each value in the window, which isO(N)space in the worst case.- Does this work when the array has negative numbers?
No. Dropping
nums[left]from the window can raise the sum, so "sum at mostlimit" is not a window that only shrinks. Use prefix sums and a hash map, as in Subarray Sum Equals K (LC 560).- How do you adapt it to count subarrays with exactly K different integers?
Replace the sum with a count of each value in the window. Shrink while the number of different values is above
limit. The answer for Subarrays with K Different Integers (LC 992) is againat_most(k) - at_most(k - 1). The measure never falls when an element is added, so the same argument holds.- Is there a one-pass version?
Yes, for some problems. A second pointer can track the start of the earliest window that still has exactly
goal, but the bookkeeping is easy to get wrong. The two-call version is shorter to write and easier to check, so it is the usual choice in an interview.
SWE Reasoning
Spot it
- The question counts subarrays with an exact value: exactly
goalas a sum, exactly K odd numbers, or exactly K different values. - Adding an element never lowers the measure, so "at most K" is a window that only shrinks from the left.
- A single window fails for "exactly K", because a zero, an even number or a repeated value changes nothing about the measure.
- The answer is a count of subarrays, not the length of the longest one.
The invariant, step by step
at_most(limit)returns 0 at once whenlimit < 0. No subarray has a measure below zero.- For each
right,window += nums[right]adds the new value to the sum ofnums[left..right]. - While
window > limit,window -= nums[left]andleft += 1drop the oldest value. The window ends as the longest one ending atrightwith a sum at mostlimit. count += right - left + 1counts every start fromlefttoright. Each of them gives a subarray ending atrightwith a sum at mostlimit.- After the last
right,countholds every subarray with a sum at mostlimit. at_most(goal) - at_most(goal - 1)removes the subarrays with a sum at mostgoal - 1from the subarrays with a sum at mostgoal. Only the subarrays with a sum of exactlygoalremain.
Traps
- Leaving out
if limit < 0: return 0. Withgoal = 0,at_most(-1)never finds a small enough sum, andleftruns off the end ofnums. - Adding 1 per step.
count += 1counts only the longest window at eachright. The answer for five zeros andgoal = 0is 5 instead of 15. - Shrinking with
ifinstead ofwhile. One drop may remove a zero, so the sum stays abovelimitand the window is counted while it is too large. - Using the template when values can be negative. The window rule needs a measure that never falls when an element is added.
- Forgetting
x % 2for odd numbers. Without it, the window adds up the numbers themselves, not the count of odd numbers.