You are given an integer array nums
and an integer k
.
The frequency of an element x
is the number of times it occurs in an array.
An array is called good if the frequency of each element in this array is less than or equal to k
.
Return the length of the longest good subarray of nums
.
A subarray is a contiguous non-empty sequence of elements within an array.
Example 1:
Input: nums = [1,2,3,1,2,3,1,2], k = 2
Output: 6
Explanation: The longest possible good subarray is [1,2,3,1,2,3] since the values 1, 2, and 3 occur at most twice in this subarray. Note that the subarrays [2,3,1,2,3,1] and [3,1,2,3,1,2] are also good.
It can be shown that there are no good subarrays with length more than 6.
Example 2:
Input: nums = [1,2,1,2,1,2,1,2], k = 1
Output: 2
Explanation: The longest possible good subarray is [1,2] since the values 1 and 2 occur at most once in this subarray. Note that the subarray [2,1] is also good.
It can be shown that there are no good subarrays with length more than 2.
Example 3:
Input: nums = [5,5,5,5,5,5,5], k = 4
Output: 4
Explanation: The longest possible good subarray is [5,5,5,5] since the value 5 occurs 4 times in this subarray.
It can be shown that there are no good subarrays with length more than 4.
Constraints:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9
1 <= k <= nums.length
The most straightforward approach is to consider every possible subarray and check if it's a "good" subarray, meaning the frequency of each element is less than or equal to k
. We keep track of the longest such subarray.
k
.Code (Python):
def longest_good_subarray_naive(nums, k):
max_len = 0
for i in range(len(nums)): # O(n)
for j in range(i, len(nums)): # O(n)
subarray = nums[i:j+1]
freq_map = {}
for num in subarray: # O(n)
freq_map[num] = freq_map.get(num, 0) + 1
is_good = True
for num in freq_map:
if freq_map[num] > k: # O(n) in worst case
is_good = False
break
if is_good:
max_len = max(max_len, len(subarray))
return max_len
Time Complexity: O(n3) in the worst case. The nested loops to generate subarrays take O(n2), and checking the frequency of each subarray takes O(n).
Space Complexity: O(n) in the worst case, to store the frequency map.
A more efficient approach involves using a sliding window. We maintain a window and a frequency map of the elements within that window. We expand the window to the right and, if any element's frequency exceeds k
, shrink the window from the left until all frequencies are within the limit.
left
and right
, both starting at 0.right
.nums[right]
.k
, shrink the window by incrementing left
and updating the frequency map accordingly.Code (Python):
def longest_good_subarray_optimal(nums, k):
freq_map = {}
left = 0
max_len = 0
for right in range(len(nums)): # O(n)
freq_map[nums[right]] = freq_map.get(nums[right], 0) + 1
while freq_map[nums[right]] > k:
freq_map[nums[left]] -= 1
if freq_map[nums[left]] == 0:
del freq_map[nums[left]] #Important to delete for space
left += 1
max_len = max(max_len, right - left + 1)
return max_len
Time Complexity: O(n), where n is the length of the input array nums
. Each element is visited at most twice (once by the right
pointer and once by the left
pointer).
Space Complexity: O(n) in the worst case, to store the frequency map. This occurs when all the elements in the array are distinct.
k
is 0, the algorithm should return 0 unless the array only contains empty values.k
.k
is greater than or equal to the length of nums
, the entire array is a good subarray, so the algorithm should return len(nums)
.