Distinct values in every window
Given an array nums and a window length k, return an array whose entry
i is the number of distinct values in nums[i .. i + k − 1].
Examples
| Input | Output | Why |
|---|---|---|
nums = [1,2,1,3,4,2,3], k = 4 | [3, 4, 4, 3] | {1,2,3}, {1,2,3,4}, {1,2,3,4}, {2,3,4}. |
nums = [4,4,4], k = 2 | [1, 1] | Every window holds one value. |
nums = [1,2,3], k = 1 | [1, 1, 1] |
Constraints
- 1 ≤ k ≤ n ≤ 10⁵
- −10⁹ ≤ nums[i] ≤ 10⁹
Hints
Hint 1
Rebuilding a set for each window is O(nk). Only two elements change between windows.
Hint 2
Why is a set not enough? Removing the leaving value is wrong if another copy is still inside.
Hint 3
Keep a map from value to its count in the window; delete a key when its count reaches 0. The map's size is the answer.
Where people lose marks
- Using a set and removing the leaving value even when another copy remains inside.
- Leaving keys with count 0 in the map, so its size overstates the answer.
Dry run on [1,2,1,3,4,2,3],4
Read it one frame at a time, the way you would trace the code on paper in an interview. Each frame shows the state after the step it describes.
-
Step 1 / 7nums10lo/hi211233442536counts1×1answer[]
1 enters.
-
Step 2 / 7nums10lo21hi1233442536counts1×1 2×1answer[]
2 enters.
-
Step 3 / 7nums10lo2112hi33442536counts1×2 2×1answer[]
1 enters.
-
Step 4 / 7nums10lo211233hi442536counts1×2 2×1 3×1answer[3]
3 enters. The window holds 3 distinct values.
-
Step 5 / 7nums1021lo123344hi2536counts1×1 2×1 3×1 4×1answer[3, 4]
4 enters. 1 leaves, but 1 copy stays inside. The window holds 4 distinct values.
-
Step 6 / 7nums102112lo334425hi36counts1×1 2×1 3×1 4×1answer[3, 4, 4]
2 enters. 2 leaves, but 1 copy stays inside. The window holds 4 distinct values.
-
Step 7 / 7nums10211233lo442536hicounts2×1 3×2 4×1answer[3, 4, 4, 3]
3 enters. 1 leaves and was its last copy, so its key is deleted. The window holds 3 distinct values.
How to think about it
The state of the window is a map from each value to the number of times it appears inside. The value entering has its count raised; the value leaving has its count lowered, and when a count reaches zero the key is deleted. The number of keys is then exactly the number of distinct values in the window.
The deletion is the step people miss. A set would answer "which values are present?" but cannot be updated when a value leaves, because it does not know whether a second copy is still in the window. Counts know.
Complexity. Time O(n) expected · Space O(k)
The solution, in six languages
Each one is compiled and run against the test table before it is published. Java is reviewed by hand.
from collections import defaultdict
def distinct_per_window(nums, k):
count = defaultdict(int) # value -> copies inside the window
out = []
for hi, x in enumerate(nums):
count[x] += 1 # x enters
if hi >= k:
y = nums[hi - k] # y leaves
count[y] -= 1
if count[y] == 0:
del count[y] # its last copy has gone
if hi >= k - 1:
out.append(len(count))
return out#include <vector>
#include <unordered_map>
std::vector<int> solve(const std::vector<int>& nums, int k) {
std::unordered_map<int, int> count; // value -> copies inside the window
std::vector<int> out;
for (int hi = 0; hi < (int)nums.size(); ++hi) {
++count[nums[hi]]; // enters
if (hi >= k) {
auto it = count.find(nums[hi - k]); // leaves
if (--it->second == 0) count.erase(it);
}
if (hi >= k - 1) out.push_back((int)count.size());
}
return out;
}import java.util.*;
class Solution {
public int[] distinctPerWindow(int[] nums, int k) {
Map<Integer, Integer> count = new HashMap<>(); // value -> copies inside the window
int[] out = new int[nums.length - k + 1];
for (int hi = 0; hi < nums.length; hi++) {
count.merge(nums[hi], 1, Integer::sum); // enters
if (hi >= k) {
int y = nums[hi - k]; // leaves
if (count.merge(y, -1, Integer::sum) == 0) count.remove(y);
}
if (hi >= k - 1) out[hi - k + 1] = count.size();
}
return out;
}
}function distinctPerWindow(nums, k) {
const count = new Map(); // value -> copies inside the window
const out = [];
for (let hi = 0; hi < nums.length; hi++) {
count.set(nums[hi], (count.get(nums[hi]) || 0) + 1); // enters
if (hi >= k) {
const y = nums[hi - k], c = count.get(y) - 1; // leaves
if (c === 0) count.delete(y); else count.set(y, c);
}
if (hi >= k - 1) out.push(count.size);
}
return out;
}use std::collections::HashMap;
fn solve(nums: &[i64], k: usize) -> Vec<usize> {
let mut count: HashMap<i64, usize> = HashMap::new(); // value -> copies inside the window
let mut out = Vec::new();
for hi in 0..nums.len() {
*count.entry(nums[hi]).or_insert(0) += 1; // enters
if hi >= k {
let y = nums[hi - k]; // leaves
let c = count.get_mut(&y).unwrap();
*c -= 1;
if *c == 0 { count.remove(&y); }
}
if hi + 1 >= k { out.push(count.len()); }
}
out
}import qualified Data.Map.Strict as M
-- Pair each value with the one leaving the window; keep counts with zeros removed.
solve :: [Int] -> Int -> [Int]
solve xs k = drop (k - 1) (map M.size (tail (scanl step M.empty pairs)))
where
pairs = zip xs (replicate k Nothing ++ map Just xs)
step m (x, out) = maybe id dec out (M.insertWith (+) x 1 m)
dec y = M.update (\c -> if c == 1 then Nothing else Just (c - 1)) y
Variants to try
- Windows of every length at once: that is a different problem, usually solved offline with sorting and a Fenwick tree.
- The most frequent value in each window: keep counts and a count of counts.
Next problems
Best total over k consecutive days
A window of fixed width slides one place at a time: add the element that enters, subtract the one that leaves.
Open the problem → Easy10 minMost vowels in any k letters
The same fixed window as a running sum, with a count of the letters that matter as its state.
Open the problem → Medium20 minEvery place an anagram begins
Compare a window's letter counts with a pattern's in O(1) per step by tracking how many letters still differ.
Open the problem →Write a solution and run it against the real test table.
Keep going
Best total over k consecutive days
A window of fixed width slides one place at a time: add the element that enters, subtract the one that leaves.
Open the problem → Easy10 minMost vowels in any k letters
The same fixed window as a running sum, with a count of the letters that matter as its state.
Open the problem → Medium20 minEvery place an anagram begins
Compare a window's letter counts with a pattern's in O(1) per step by tracking how many letters still differ.
Open the problem → Sliding windowThe pattern behind it
A window that grows on the right, shrinks on the left, and holds an invariant at all times.
Read the pattern →Preparing for a real process? Quant interview preparation, or book a free 20-minute call.