Taro Logo

Length of Longest Subarray With at Most K Frequency

Medium
Meta logo
Meta
1 view
Topics:
ArraysSliding WindowsTwo Pointers

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

Solution


Naive Solution

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.

  1. Iterate through all possible start indices of subarrays.
  2. For each start index, iterate through all possible end indices.
  3. For each subarray, create a frequency map (e.g., using a hash map).
  4. Check if all frequencies are less than or equal to k.
  5. If the subarray is "good," update the maximum length found so far.

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.

Optimal Solution

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.

  1. Initialize a frequency map and two pointers, left and right, both starting at 0.
  2. Expand the window by incrementing right.
  3. Update the frequency map with the new element at nums[right].
  4. While any frequency in the map is greater than k, shrink the window by incrementing left and updating the frequency map accordingly.
  5. Update the maximum length of the "good" subarray.

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.

Edge Cases

  1. Empty array: The algorithm should return 0 if the input array is empty.
  2. k = 0: If k is 0, the algorithm should return 0 unless the array only contains empty values.
  3. All elements are the same: If all elements in the array are the same, the longest good subarray will have a length of k.
  4. k >= len(nums): If 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).
  5. nums is null: Check if nums is null