Count Subarrays Where Max Element Appears at Least K Times
MedPrompt
You are given an integer array nums and a positive integer k.
Return the number of subarrays where the maximum element of nums appears at least k times in that subarray.
A subarray is a contiguous sequence of elements within an array.
Example 1:
Input: nums = [1,3,2,3,3], k = 2
Output: 6
Explanation: The subarrays that contain the element 3 at least 2 times are: [1,3,2,3], [1,3,2,3,3], [3,2,3], [3,2,3,3], [2,3,3] and [3,3].Example 2:
Input: nums = [1,4,2,1], k = 3
Output: 0
Explanation: No subarray contains the element 4 at least 3 times.
Constraints:
1 <= nums.length <= 1051 <= nums[i] <= 1061 <= k <= 105
Approaches
2 approaches with complexity analysis and trade-offs.
This approach involves systematically checking every possible contiguous subarray within the nums array. We use two nested loops to define the start and end of each subarray. For each subarray, we count the occurrences of the global maximum element and check if the count meets the k threshold.
Algorithm
- First, find the maximum element
maxValin the entirenumsarray. - Initialize a result counter,
ans, to zero. - Use a nested loop to iterate through all possible subarrays. The outer loop with index
idefines the start of the subarray, and the inner loop with indexjdefines the end. - For each starting position
i, initialize a countercurrentMaxCountto 0. - As the inner loop for
jprogresses fromiton-1, check ifnums[j]is equal tomaxVal. If it is, incrementcurrentMaxCount. - If
currentMaxCountbecomes greater than or equal tok, it means the subarraynums[i...j]is valid. From this point on, for this fixedi, all subsequent subarraysnums[i...j+1],nums[i...j+2], etc., will also be valid. - Therefore, once
currentMaxCount >= k, we can add the remaining number of subarrays, which isn - j, to ouransand break the inner loop to move to the nexti. - After iterating through all possible starting points
i,answill hold the total count.
Walkthrough
The brute-force method is the most straightforward way to solve the problem.
- First, we iterate through the entire array to find its maximum element, let's call it
maxVal. - We then initialize a variable
ansto 0, which will store our final count of valid subarrays. - We use a pair of nested loops. The outer loop, with index
i, iterates from0ton-1and sets the starting point of our subarrays. - The inner loop, with index
j, iterates fromiton-1, setting the ending point. This(i, j)pair defines the subarraynums[i...j]. - For each subarray, we count the number of times
maxValappears. To optimize this slightly from a naive O(N^3) approach, we can maintain a running count ofmaxValwithin the inner loop. - When the count of
maxValinnums[i...j]reaches or exceedsk, we increment ourans. A further optimization is to notice that ifnums[i...j]is valid, so isnums[i...j+1],nums[i...j+2], etc. So, once a valid subarray is found, we can add all remaining extensions to the answer and break the inner loop.
class Solution { public long countSubarrays(int[] nums, int k) { int maxVal = 0; for (int num : nums) { maxVal = Math.max(maxVal, num); } long ans = 0; int n = nums.length; for (int i = 0; i < n; i++) { int currentMaxCount = 0; for (int j = i; j < n; j++) { if (nums[j] == maxVal) { currentMaxCount++; } if (currentMaxCount >= k) { // If nums[i..j] is valid, then nums[i..j+1], nums[i..j+2], ..., nums[i..n-1] are also valid. // There are (n-1) - j + 1 = n - j such subarrays. ans += (n - j); break; // Move to the next starting position i } } } return ans; }}Complexity
Time
O(N^2) in the worst case. Finding the maximum element takes O(N). The nested loops run in O(N^2) time, leading to a total complexity dominated by the loops.
Space
O(1), as we only use a few variables to store the maximum value, counters, and loop indices.
Trade-offs
Pros
The logic is simple and easy to understand.
It's a direct implementation of the problem statement.
Cons
The O(N^2) time complexity is too slow for the given constraints (N up to 10^5) and will result in a 'Time Limit Exceeded' error on most platforms.
A much more efficient solution uses the sliding window technique. The core idea is to maintain a window [left...right] and efficiently count the number of valid subarrays as we iterate through the array with the right pointer. For each position right, we determine how many subarrays ending at right satisfy the condition.
Algorithm
- First, find the maximum element
maxValin thenumsarray. - Initialize
ans = 0, aleftpointer to0, andmaxCountInWindow = 0. - Iterate through the array with a
rightpointer from0ton-1. - At each
right, ifnums[right] == maxVal, incrementmaxCountInWindow. - Now, use a
whileloop to shrink the window from the left. As long as the current window[left...right]is valid (i.e.,maxCountInWindow >= k), we shrink it by incrementingleft. If the element at the oldleftposition wasmaxVal, we decrementmaxCountInWindow. - After the
whileloop finishes,leftpoints to the first indexpsuch that the window[p...right]has fewer thankmaximum elements. This implies that any subarray ending atrightwith a starting indexifrom0top-1is a valid subarray. - The number of such valid starting indices is
p, which is the current value ofleft. So, we addleftto our totalans. - Repeat this for all
rightpointers. - Return
ans.
Walkthrough
This optimal approach leverages a sliding window to achieve linear time complexity. Instead of re-calculating for every subarray, we maintain a window and update our counts as the window slides.
The key insight is to count, for each ending position right, how many valid subarrays [i...right] exist. A subarray is valid if it has at least k occurrences of the array's maximum element (maxVal).
We use two pointers, left and right, to define our sliding window. We expand the window by moving right one step at a time. If nums[right] is maxVal, we increment a counter for maxVal within the window.
Whenever our window [left...right] becomes 'valid' (i.e., contains k or more maxVals), we know that any subarray starting at an index i <= left and ending at right is also valid. To simplify counting, we can find the minimal valid window.
A cleaner way to count is to observe the following: for a fixed right, we advance left as much as possible while the window [left...right] remains valid. Let's say we stop at left = p. This means any subarray starting at 0, 1, ..., p-1 and ending at right is a valid subarray. There are p such subarrays. So, for each right, we add the current left pointer's value to our answer.
class Solution { public long countSubarrays(int[] nums, int k) { int maxVal = 0; for (int num : nums) { maxVal = Math.max(maxVal, num); } long ans = 0; int left = 0; int maxCountInWindow = 0; int n = nums.length; for (int right = 0; right < n; right++) { if (nums[right] == maxVal) { maxCountInWindow++; } // While the window is valid (has at least k max elements), // shrink it from the left to find the boundary. while (maxCountInWindow >= k) { if (nums[left] == maxVal) { maxCountInWindow--; } left++; } // For the current 'right', any subarray ending at 'right' with a starting // index from 0 to 'left - 1' is a valid subarray. The number of such // starting points is 'left'. ans += left; } return ans; }}Complexity
Time
O(N). Finding the maximum element is O(N). The main loop involves two pointers, `left` and `right`, each traversing the array at most once. This results in a total time complexity of O(N).
Space
O(1), as it only requires a few variables for the pointers and the count, irrespective of the input size.
Trade-offs
Pros
Extremely efficient with O(N) time complexity.
Uses constant extra space, making it optimal in terms of memory usage.
Cons
The logic, while efficient, can be less intuitive to grasp initially compared to the brute-force method.
Solutions
Solution
class Solution {public long countSubarrays(int[] nums, int k) { int mx = Arrays.stream(nums).max().getAsInt(); int n = nums.length; long ans = 0; int cnt = 0, j = 0; for (int x : nums) { while (j < n && cnt < k) { cnt += nums[j++] == mx ? 1 : 0; } if (cnt < k) { break; } ans += n - j + 1; cnt -= x == mx ? 1 : 0; } return ans; }}class Solution {public: long long countSubarrays(vector<int> &nums, int k) { int mx = *max_element(nums.begin(), nums.end()); int n = nums.size(); long long ans = 0; int cnt = 0, j = 0; for (int x : nums) { while (j < n && cnt < k) { cnt += nums[j++] == mx; } if (cnt < k) { break; } ans += n - j + 1; cnt -= x == mx; } return ans; }};class Solution: def countSubarrays(self, nums: List[int], k: int) -> int: mx = max(nums) n = len(nums) ans = cnt = j = 0 for x in nums: while j < n and cnt < k: cnt += nums[j] == mx j += 1 if cnt < k: break ans += n - j + 1 cnt -= x == mx return ansVideo walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.