Count the Number of Good Subarrays
MediumUpdated: Aug 2, 2025
Practice on:
Problem
Given an integer array nums and an integer k, return the number of good subarrays of nums.
A subarray arr is good if there are at leastk pairs of indices (i, j) such that i < j and arr[i] == arr[j].
A subarray is a contiguous non-empty sequence of elements within an array.
Examples
Example 1
Input: nums = [1,1,1,1,1], k = 10
Output: 1
Explanation: The only good subarray is the array nums itself.
Example 2
Input: nums = [3,1,4,3,2,2,4], k = 2
Output: 4
Explanation: There are 4 different good subarrays:
- [3,1,4,3,2,2] that has 2 pairs.
- [3,1,4,3,2,2,4] that has 3 pairs.
- [1,4,3,2,2,4] that has 2 pairs.
- [4,3,2,2,4] that has 2 pairs.
Constraints
1 <= nums.length <= 10^51 <= nums[i], k <= 10^9
Solution
Method 1 - Sliding window technique
To solve the problem efficiently, we can use the sliding window approach combined with a hash map to track the frequency of elements in the current subarray window. The idea involves expanding the window to form valid subarrays (those with at least k good pairs) and contracting the window once the condition is violated.
Here is the approach:
- Use two pointers (
startandend) to maintain the sliding window. - Maintain a frequency count of elements in the current window using a hash map.
- For each new element added to the window (
endpointer increment), calculate the number of new good pairs added based on its frequency and update the total count. - Keep shrinking the window (increment the
startpointer) when the count of good pairs in the window meets or exceedsk. During shrinking, adjust frequencies and good pairs accordingly. - Accumulate the number of good subarrays across all valid windows.
Code
Java
class Solution {
public long countGood(int[] nums, int k) {
long ans = 0; // Use long as the return type
int start = 0;
long pairs = 0; // Track the number of pairs
Map<Integer, Integer> freq = new HashMap<>(); // Frequency map
for (int end = 0; end < nums.length; end++) {
int curr = nums[end];
freq.put(curr, freq.getOrDefault(curr, 0) + 1);
// Calculate pairs formed by current element
pairs += freq.get(curr) - 1;
// Shrink the window while condition is met
while (pairs >= k) {
ans += nums.length - end; // Count all subarrays ending at 'end'
int left = nums[start];
pairs -= freq.get(left) - 1; // Remove pairs formed by 'left'
freq.put(left, freq.get(left) - 1);
start++;
}
}
return ans;
}
}
Python
class Solution:
def countGood(self, nums: List[int], k: int) -> int:
ans = 0
start = 0
pairs = 0
freq: dict[int, int] = {}
for end, curr in enumerate(nums):
freq[curr] = freq.get(curr, 0) + 1
# Calculate pairs formed by current element
pairs += freq[curr] - 1
# Shrink window
while pairs >= k:
ans += len(nums) - end # Count good subarrays ending at `end`
left = nums[start]
pairs -= freq[left] - 1 # Remove pairs formed by `left`
freq[left] -= 1
start += 1
return ans
Complexity
- ⏰ Time complexity:
O(n). Thestartandendpointers both traverse thenumsarray once, making the time complexityO(n). - Operations on the hash map (
freq) such as updating and accessing elements areO(1)due to average-case behaviour. - 🧺 Space complexity:
O(u). - The hash map (freq) stores counts of unique elements in the array. In the worst case, all elements are unique, making itO(u), whereuis the number of unique elements.