🧠
Sliding Window with Two Pointers
💡 This approach uses two pointers to find the number of subarrays with exactly k odd numbers by counting subarrays with at most k and at most k-1 odd numbers and subtracting them.
Intuition
Number of subarrays with exactly k odd numbers = number with at most k odds - number with at most k-1 odds. Use sliding window to count at most k odd subarrays efficiently.
Algorithm
- Define a helper function to count subarrays with at most k odd numbers using two pointers.
- Initialize two pointers and a count of odd numbers in the window.
- Expand right pointer, update odd count.
- Shrink left pointer while odd count exceeds k.
- Sum counts of valid subarrays ending at each right pointer.
- Return count(at most k) - count(at most k-1) as final result.
💡 This approach cleverly uses the difference of two counts to find exact matches, avoiding explicit enumeration of all subarrays.
def at_most_k(nums, k):
left = 0
count = 0
odd_count = 0
for right in range(len(nums)):
if nums[right] % 2 == 1:
odd_count += 1
while odd_count > k:
if nums[left] % 2 == 1:
odd_count -= 1
left += 1
count += right - left + 1
return count
def count_nice_subarrays(nums, k):
return at_most_k(nums, k) - at_most_k(nums, k - 1)
# Driver code
if __name__ == '__main__':
print(count_nice_subarrays([1,1,2,1,1], 3)) # Output: 2
Line Notes
def at_most_k(nums, k):Helper counts subarrays with at most k odd numbers
left = 0Initialize left pointer of sliding window
if nums[right] % 2 == 1:Increment odd count when odd found
while odd_count > k:Shrink window from left if too many odds
count += right - left + 1Add number of valid subarrays ending at right
return at_most_k(nums, k) - at_most_k(nums, k - 1)Difference gives count of exactly k odd subarrays
public class Solution {
private int atMostK(int[] nums, int k) {
int left = 0, count = 0, oddCount = 0;
for (int right = 0; right < nums.length; right++) {
if (nums[right] % 2 == 1) {
oddCount++;
}
while (oddCount > k) {
if (nums[left] % 2 == 1) {
oddCount--;
}
left++;
}
count += right - left + 1;
}
return count;
}
public int countNiceSubarrays(int[] nums, int k) {
return atMostK(nums, k) - atMostK(nums, k - 1);
}
public static void main(String[] args) {
Solution sol = new Solution();
System.out.println(sol.countNiceSubarrays(new int[]{1,1,2,1,1}, 3)); // Output: 2
}
}
Line Notes
private int atMostK(int[] nums, int k) {Helper counts subarrays with at most k odd numbers
int left = 0, count = 0, oddCount = 0;Initialize pointers and counters
if (nums[right] % 2 == 1) {Increment odd count if odd number found
while (oddCount > k) {Shrink window from left if too many odds
count += right - left + 1;Add valid subarrays ending at right
return atMostK(nums, k) - atMostK(nums, k - 1);Difference gives exact count
#include <iostream>
#include <vector>
using namespace std;
int atMostK(const vector<int>& nums, int k) {
int left = 0, count = 0, oddCount = 0;
for (int right = 0; right < (int)nums.size(); right++) {
if (nums[right] % 2 == 1) {
oddCount++;
}
while (oddCount > k) {
if (nums[left] % 2 == 1) {
oddCount--;
}
left++;
}
count += right - left + 1;
}
return count;
}
int countNiceSubarrays(vector<int>& nums, int k) {
return atMostK(nums, k) - atMostK(nums, k - 1);
}
int main() {
vector<int> nums = {1,1,2,1,1};
cout << countNiceSubarrays(nums, 3) << endl; // Output: 2
return 0;
}
Line Notes
int atMostK(const vector<int>& nums, int k) {Helper counts subarrays with at most k odd numbers
int left = 0, count = 0, oddCount = 0;Initialize pointers and counters
if (nums[right] % 2 == 1) {Increment odd count if odd
while (oddCount > k) {Shrink window from left if too many odds
count += right - left + 1;Add valid subarrays ending at right
return atMostK(nums, k) - atMostK(nums, k - 1);Difference gives exact count
function atMostK(nums, k) {
let left = 0, count = 0, oddCount = 0;
for (let right = 0; right < nums.length; right++) {
if (nums[right] % 2 === 1) {
oddCount++;
}
while (oddCount > k) {
if (nums[left] % 2 === 1) {
oddCount--;
}
left++;
}
count += right - left + 1;
}
return count;
}
function countNiceSubarrays(nums, k) {
return atMostK(nums, k) - atMostK(nums, k - 1);
}
// Test
console.log(countNiceSubarrays([1,1,2,1,1], 3)); // Output: 2
Line Notes
function atMostK(nums, k) {Helper counts subarrays with at most k odd numbers
let left = 0, count = 0, oddCount = 0;Initialize pointers and counters
if (nums[right] % 2 === 1) {Increment odd count if odd
while (oddCount > k) {Shrink window from left if too many odds
count += right - left + 1;Add valid subarrays ending at right
return atMostK(nums, k) - atMostK(nums, k - 1);Difference gives exact count
Each element is visited at most twice by left and right pointers, so O(n) time. Only constant extra space used.
💡 For n=100000, this means about 200000 operations, which is efficient and uses less memory than hashmap.
Interview Verdict: Accepted
This approach is efficient and uses constant space, making it a good alternative in interviews.