An array is nice if it contains exactly k odd numbers. Given an integer array nums and an integer k, return the number of nice contiguous subarrays.
Input: nums = [1,1,2,1,1], k = 3 Output: 2 The subarrays with exactly 3 odd numbers are [1,1,2,1] and [1,2,1,1].
Input: nums = [2,2,2,1,2,2,1,2,2,2], k = 2 Output: 16 There are 16 subarrays containing exactly 2 odd numbers.
- 1 <= nums.length <= 5 * 10^4 - 1 <= nums[i] <= 10^5 - 1 <= k <= nums.length
Replace every odd number with a 1 and every even with a 0, and "exactly k odd numbers" becomes "sum exactly k" — the very same counting problem as Binary Subarrays With Sum. So use the same identity: exactly k = atMost(k) − atMost(k − 1), where atMost(m) counts subarrays with at most m odd numbers via a sliding window.
“Exactly k odds, or at least k?”
Exactly k — no more, no fewer.
“Do even numbers matter?”
Only as filler — they never change the odd count, but they do extend valid subarrays.
If I map odd to 1 and even to 0, a nice subarray is one whose sum is exactly k.
That's exactly Binary Subarrays With Sum, so I count atMost(k) minus atMost(k-1).
atMost(m) is a window counting subarrays with at most m odd numbers, adding right-left+1 per step.
Worked example — nums = [1,1,2,1,1], k = 3
odd/even -> [1,1,0,1,1], want sum exactly 3 atMost(3) - atMost(2) = 2 the two nice subarrays are [1,1,2,1] and [1,2,1,1]
Map odd -> 1, even -> 0. "Exactly k odd numbers" is "sum exactly k", identical to Binary Subarrays With Sum.
Counting exactly k directly is hard; two at-most counts subtract to leave precisely the subarrays with k odds.
Track odds in the window; shrink from the left whenever it exceeds m; add right - left + 1 per step. Even numbers just widen the window for free.
| Count odds in every subarray | atMost(k) − atMost(k−1) | |
|---|---|---|
| Idea | Accumulate the odd count per subarray, count == k | Two sliding-window at-most-odds counts, subtracted |
| Time | O(n^2) | O(n) |
| Space | O(1) | O(1) |
The brute counts odds in every subarray; the identity replaces it with two linear passes. Full code is in the Approaches selector below.
Key takeaway
Odd = 1, even = 0, and "exactly k odd numbers" is "sum exactly k". Count it as atMost(k) - atMost(k - 1), where atMost(m) slides a window over the odd count, adding right - left + 1 and shrinking when the odd count exceeds m. Two O(n) passes.
def atMost(m):
if m < 0: return 0
left = 0; total = 0; odds = 0
for right in 0 .. n-1:
if nums[right] is odd: odds += 1
while odds > m: if nums[left] is odd: odds -= 1; left += 1
total += right - left + 1
return total
return atMost(k) - atMost(k - 1)