Problem overview
The method longestSubarray receives an array of integers, nums, and a limit K. It returns the length of the longest subarray, a run of neighbouring elements, that contains at most K different values. Repeated values inside the run are fine: only the number of different values is limited.
In the example, nums = [1, 2, 1, 2, 3] and K = 2. The first four elements, [1, 2, 1, 2], contain two distinct values, 1 and 2. Adding the final 3 would make it three, so the answer is 4.
Approach: try every start, grow until too many distinct values
The statement allows O(n²) time and O(n) extra space. Fix the start i of the subarray, then move the end j to the right one element at a time. A set records the values seen from i to j, and its size is the number of distinct values in the subarray.
The useful fact is that a longer subarray can never have fewer distinct values than a shorter one that it contains. So once size exceeds K, extending further is pointless for this start, and the inner loop can stop. The next start then begins with a fresh, empty set.
Checking every subarray from scratch would cost O(n³), since there are about n² of them and counting each one's distinct values takes up to n steps. Growing a single subarray and keeping the set updated removes that last factor.
The example, start by start
With K = 2 on [1, 2, 1, 2, 3]:
- From
i = 0the set stays at size 2 through index 3, so the lengths 1, 2, 3 and 4 are recorded. At index 4 the value3makes the size 3, and the loop breaks. The best so far is 4. - From
i = 1the subarray[2, 1, 2]is valid with length 3, and then the3breaks the loop. - From
i = 2andi = 3the best lengths are 2 and 2. - From
i = 4the single element[3]gives length 1.
The answer stays at 4. Most of the later starts repeat work already done, which is what the faster approaches for this problem avoid.
Step by step
- Start the answer at 0.
answeris declared withlet. - Loop over the start
ifrom0whilei < nums.length. - Create an empty set for this start.
distinctValuesis anew Set<number>(). The type argument tells the compiler what the set will hold, because an empty set gives it nothing to infer. - Loop over the end
jfromi. Each pass extends the subarray by one element. - Add
nums[j]to the set. Duplicates are ignored, so the size counts distinct values. - Stop when the set is too big. If
distinctValues.size > K,break. Nothing longer from this start can be valid. - Record the length. The subarray
nums[i..j]is valid, with lengthj - i + 1, soanswerbecomes the larger of the two. - Return
answerafter the outer loop.
Notice the order: the element is added first and the size is tested afterwards, because the test needs the new element counted. A check that only asks whether a value repeats would go the other way round.
The solution in TypeScript
class Solution {
longestSubarray(nums: number[], K: number): number {
let answer = 0;
for (let i = 0; i < nums.length; i++) {
let distinctValues = new Set<number>();
for (let j = i; j < nums.length; j++) {
distinctValues.add(nums[j]);
if (distinctValues.size > K) {
break;
}
answer = Math.max(answer, j - i + 1);
}
}
return answer;
}
}
size, notlength. ASetreports its number of values with the propertysize. WritingdistinctValues.lengthgivesundefined, and the compiler reports an error.- The parameter is a capital
K. Names are case-sensitive, sokwould be a different, undeclared name. - Sets ignore repeats. Adding a value that is already present does nothing, which is why a subarray like
[1, 2, 1, 2]has a set of size 2. - The set is rebuilt for each start. It is declared inside the outer loop on purpose.
Complexity
Time: O(n²). There are n starts, and each inner loop runs at most n times with constant average cost per step. The break usually ends it sooner, but in the worst case, when the whole array has at most K distinct values, it never fires.
Space: O(n). The set holds at most K + 1 values before the break, and never more than n.
Edge cases and common mistakes
- Empty array. The loops never run and the method returns
0. Kis 0. The first element already makes the size 1, which is more than 0, so every start breaks at once and the answer is0.Kat least the number of distinct values. The whole array qualifies and the answer isnums.length. For[1, 2, 3]withK = 5, that is3.- Using
>=in the test. That would reject subarrays with exactlyKdistinct values. The limit is "at most", so only>breaks. - Updating the answer before the test. The invalid subarray would be counted, giving
5instead of4on the example. - Sharing one set across starts. Leftover values from earlier starts would inflate the size.
Testing
nums = [1, 2, 1, 2, 3],K = 2gives4.nums = [1, 2, 3],K = 0gives0.nums = [4, 4, 4],K = 1gives3.nums = [],K = 3gives0.nums = [1, 2, 3],K = 5gives3.