2962. Count Subarrays Where Max Element Appears at Least K Times
Problem Description
You have an array of integers, nums
, and a positive integer, k
. Your task is to find out how many subarrays exist within this array where the highest number in the array, which we can call mx
, shows up at least k
times. A subarray is defined simply as a continuous portion of the array; it does not need to include all elements, but the elements it does include must be in the same order they appear in the full nums
array.
Intuition
To solve this problem, we need to think about how we can track occurrences of the maximum element in each subarray. A direct approach to identifying subarrays would involve looking at every possible subarray, which would be inefficient especially for larger arrays. A smarter way to do this is by using the concept of a moving window, often known as the two-pointer technique.
We define a variable mx
to hold the maximum value in the array and then attempt to find subarrays that have at least k
occurrences of mx
. Starting with the first element, we expand our window to the right until we have k
instances of mx
. When we reach that point, any larger subarray that includes this window will also have at least k
instances of mx
. Thus, we add the total possible subarrays to our answer by calculating the number of elements from the end of our window to the end of the array, inclusive. We then slide our window to the right by one and reduce our count of mx
occurrences if the first element of our previous window was a mx
.
The beauty of this approach lies in its linear time complexity, as each element is visited only once by each of the two pointers. We count the occurrences of mx
within a current window and continuously adjust the window size, keeping track of how many subarrays meet our requirement. This helps us avoid reexamining portions of the array unnecessarily, turning what could be an overwhelmingly complex problem into a manageable one.
Learn more about Sliding Window patterns.
Solution Approach
The solution implements a two-pointer algorithm to efficiently find the number of subarrays fulfilling the given condition. We use the variables i
and j
to represent our two pointers, which define the edges of our current window on the array. The variable cnt
serves as a counter for how many times mx
(the maximum element of the entire array) appears within this window. The solution also initializes an answer variable ans
to accumulate the number of valid subarrays.
We start by finding the maximum element in the array, mx = max(nums)
, since the problem specifically concerns occurrences of this element.
Next, we iterate through each element in the array with a for-loop, thinking of each element as the potential start of a subarray (this is the left pointer, i
). As we iterate:
- We use the right pointer
j
to extend the window to the right, incrementingcnt
whenever we encounter the maximum valuemx
. - We continue extending the window until
cnt
is at leastk
, meaning we've found a subarray wheremx
appears at leastk
times. - At this point, any subarray starting from
i
and ending at or pastj-1
will contain at leastk
instances ofmx
. The total such subarrays can be counted asn - (j - 1)
, and we add this count to our answerans
. - Before moving
i
to the next position, reducing our window from the left, we need to decreasecnt
if the elementnums[i]
that's being removed is equal tomx
.
The loop breaks if j
reaches the end of the array, or if cnt
falls below k
, because further iteration of i
cannot possibly find a sufficient count of mx
to fulfill the condition.
This approach only passes through the array once with each pointer and hence, has a linear time complexity of O(n), where n is the number of elements in the array.
Here's the implementation walkthrough based on the given solution:
mx = max(nums)
: Find the maximum element innums
.n = len(nums)
: Get the length of the array to know when we've reached the end.ans = cnt = j = 0
: Initialize the answer (ans
), the counter for the maximum value (cnt
), and the right pointer (j
) to zero.for x in nums
: Start the main loop overnums
, considering eachx
to be the potential beginning of a subarray.while j < n and cnt < k
: Expand the window to the right with the right pointerj
untilk
instances ofmx
are within it or the end of the array is reached.cnt += nums[j] == mx
: Incrementcnt
when the current element ismx
.j += 1
: Move the right pointerj
to the next position.if cnt < k
: Ifk
or more instances ofmx
cannot be found, no more valid subarrays are possible, break the loop.ans += n - j + 1
: Add the number of valid subarrays toans
.cnt -= x == mx
: Before the next iteration of the loop, decreasecnt
if the element removed from the current window ismx
.
By using these steps, we obtain a seamless, efficient algorithm that finds the total number of valid subarrays without resorting to brute force.
Ready to land your dream job?
Unlock your dream job with a 2-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's consider a small example to illustrate the solution approach. Suppose the array nums
is [1, 2, 2, 3, 2]
, and k
is 2. We want to count the subarrays where the maximum element appears at least twice.
First, we find the maximum element mx
of nums
: mx = max(nums) = 3
.
Now, we initialize ans = 0
, cnt = 0
, and j = 0
, and start iterating over the array with our left pointer, represented by the elements in the for-loop.
Here's the step-by-step process:
- We start at
i = 0
, withnums[i] = 1
. - We look to expand our window to the right; since
cnt < k
, our inner while-loop starts. - We increment
j
until we encountermx
at leastk
times. During the process:, we incrementcnt
whenevernums[j] == mx
.j = 0
,nums[j] = 1
,cnt
remains0
.j = 1
,nums[j] = 2
,cnt
remains0
.j = 2
,nums[j] = 2
,cnt
remains0
.j = 3
,nums[j] = 3
,cnt
becomes1
.j = 4
,nums[j] = 2
,cnt
remains1
.- As
j
has reached the end of the array and we have not foundk
instances ofmx
, we break from the loop and no subarrays are counted from this position (ans
remains0
).
- We move to the next starting position,
i = 1
.- Repeat steps 2-3, but since
j
is already at the end, we don't enter the while-loop, and no subarrays are counted from this position either.
- Repeat steps 2-3, but since
- We continue this process moving
i
from0
ton-1
(end of the array), but in this case, sincemx
appears only once and ourk
condition is at least twice, we don't find any subarray that matches the criteria.
In the end, the ans
is 0
because there are no subarrays within [1, 2, 2, 3, 2]
where the maximum element 3
appears at least 2
times.
Had mx
appeared at least k = 2
times, we would add up all the possible subarrays from i
up to j - 1
to our answer ans
each time, and if nums[i]
is an instance of mx
, we would decrease cnt
before moving on to the next i
.
Solution Implementation
1from typing import List # Added for List type hint support.
2
3class Solution:
4 def count_subarrays(self, nums: List[int], k: int) -> int:
5 max_value = max(nums) # Find the maximum value in the nums list.
6 n = len(nums) # Get the length of the nums list.
7 answer = 0 # Initialize the answer to 0.
8 count_max = 0 # Initialize the count for the maximum value to 0.
9 j = 0 # Pointer to scan through the list.
10
11 # Iterate over all elements in nums array.
12 for i, x in enumerate(nums):
13 # Move the j pointer and update count_max until we have counted k max_value or reached end of list.
14 while j < n and count_max < k:
15 count_max += nums[j] == max_value # Increment count_max if nums[j] is max_value.
16 j += 1 # Increment j to move the window forward.
17
18 # If we have counted k max_value, update the answer.
19 if count_max < k:
20 break # If count_max is less than k, then break from the loop as further windows won't have k max_values.
21 answer += n - j + 1 # Add the number of valid subarrays starting from i-th position.
22
23 # Decrease count_max for the sliding window as we move past the i-th element.
24 count_max -= x == max_value
25
26 return answer # Return the total number of valid subarrays.
27
28# Example of using the modified Solution class.
29sol = Solution()
30result = sol.count_subarrays([1, 4, 2, 5, 3], 2)
31print(result) # Output the result.
32
1class Solution {
2 public long countSubarrays(int[] nums, int k) {
3 // Find the maximum value in the array
4 int maxNum = Arrays.stream(nums).max().getAsInt();
5
6 // Initialize the length of the array
7 int n = nums.length;
8
9 // Variable to store the answer
10 long countOfSubarrays = 0;
11
12 // Initialize the count of maximum number in the current window
13 int maxCount = 0;
14
15 // Initialize the right pointer for the window to 0
16 int rightPointer = 0;
17
18 // Iterate over each element in the nums array with index represented by leftPointer
19 for (int leftPointer = 0; leftPointer < n; leftPointer++) {
20 // Expand the window until we have less than k occurrences of the maximum number
21 while (rightPointer < n && maxCount < k) {
22 // Increase the count if the current number is the maximum number
23 if (nums[rightPointer] == maxNum) {
24 maxCount++;
25 }
26 // Move the right pointer to the right
27 rightPointer++;
28 }
29
30 // If we have found k or more occurrences, we cannot form more subarrays, so we break out
31 if (maxCount < k) {
32 break;
33 }
34
35 // Update the count of subarrays with the number of ways to choose the end point of subarrays
36 countOfSubarrays += n - rightPointer + 1;
37
38 // Exclude the current left pointer number from count if it's the maximum number
39 if (nums[leftPointer] == maxNum) {
40 maxCount--;
41 }
42 }
43
44 // Return the total number of subarrays
45 return countOfSubarrays;
46 }
47}
48
1class Solution {
2public:
3 long long countSubarrays(vector<int>& nums, int k) {
4 // Find the maximum value in the array
5 int maxVal = *max_element(nums.begin(), nums.end());
6 int n = nums.size();
7 long long answer = 0; // This will store the final count of subarrays
8 int countMax = 0; // Counts the number of maximum elements in the current subarray
9 int j = 0; // Pointer to extend the right boundary of the subarray
10
11 // Iterate through the array, considering each element as the start of a subarray
12 for (int i = 0; i < n; ++i) {
13 // Extend the subarray until we either run out of elements
14 // or we have 'k' instances of the maximum element
15 while (j < n && countMax < k) {
16 countMax += nums[j] == maxVal;
17 ++j;
18 }
19 // If we have less than 'k' instances, we cannot form more subarrays
20 // starting from the current element
21 if (countMax < k) break;
22
23 // Add the number of possible subarrays that start with the current element
24 answer += n - j + 1;
25
26 // Prepare for the next iteration by decrementing the count if the current
27 // element is the maximum value
28 countMax -= nums[i] == maxVal;
29 }
30
31 // Return the total count of qualifying subarrays
32 return answer;
33 }
34};
35
1// Counts subarrays where the occurrence of the maximum element in the array is less than k
2function countSubarrays(nums: number[], k: number): number {
3 // Find the maximum value in the input array nums
4 const maximumValue = Math.max(...nums);
5 // Get the length of the input array nums
6 const len = nums.length;
7 // Initialize count of maximum values and index pointer
8 let countOfMax = 0;
9 let indexPointer = 0;
10 // Initialize answer which will hold the count of valid subarrays
11 let answer = 0;
12
13 // Iterate over each element in the nums array
14 for (const current of nums) {
15 // Increase indexPointer while total max-element count is less than k
16 // and the indexPointer is within the array bounds
17 while (indexPointer < len && countOfMax < k) {
18 if (nums[indexPointer] === maximumValue) {
19 countOfMax += 1;
20 }
21 indexPointer += 1;
22 }
23
24 // If the count is less than k at this point, we can exit the loop
25 if (countOfMax < k) {
26 break;
27 }
28
29 // Add the number of valid subarrays that can be formed from this position.
30 answer += len - indexPointer + 1;
31
32 // Decrease the count of maximum values if the current element was a maximum
33 if (current === maximumValue) {
34 countOfMax -= 1;
35 }
36 }
37
38 // Return the total number of subarrays that satisfy the condition
39 return answer;
40}
41
Time and Space Complexity
The time complexity of the given code is O(n)
, where n
is the length of the nums
array. This is because the algorithm uses a sliding window that iterates over each element of the array at most twice - once when extending the window and once when moving the starting point of the window forward. Thus, each element is visited a constant number of times, leading to a linear time complexity with respect to the size of the input array.
The space complexity is O(1)
as the algorithm only uses a constant amount of additional space for variables like mx
, n
, ans
, cnt
, and j
, irrespective of the size of the input array.
Learn more about how to find time and space complexity quickly using problem constraints.
What is the best way of checking if an element exists in an unsorted array once in terms of time complexity? Select the best that applies.
Recommended Readings
https algomonster s3 us east 2 amazonaws com cover_photos stack svg Sliding Window Maximum Monotonic Stack We have an array and a sliding window defined by a start index and an end index The sliding window moves from left of the array to right There are always k elements in
LeetCode Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way we
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https algomonster s3 us east 2 amazonaws com recursion jpg You first
Want a Structured Path to Master System Design Too? Don’t Miss This!