Hashing & Sets · Hashing Fundamentals

Sliding Window Distinct Count

Sliding a fixed-size window across an array while maintaining a running frequency map of exactly what's inside it, incrementing the entering element and decrementing (removing at zero) the exiting element on every shift, so the map's size at any moment is the window's distinct count without rescanning it.

  • 5 min read
  • 9 practice questions
  • 2 graded problems
On this page
  1. Description
  2. The approach
  3. A second flavor: exactly k = at most k minus at most k - 1
  4. Where you'll see it
  5. Which tool for which job?
  6. Common mistakes
  7. Check yourself
  8. Key takeaways
  9. Quick revise
  10. Practice

Description

"How many distinct values are in each window of k elements?" Recounting every window from scratch costs O(k) per window. Moving the window one step changes only two things: one value comes in on the right and one leaves on the left. So the answer can be updated instead of recomputed, by keeping a count per value and removing values whose count drops to zero. The same bookkeeping, with the "at most k" trick, counts subarrays with exactly k distinct values.

We'll go from simple to more technical:

  1. The pattern: a fixed window with a frequency map, updated as values enter and leave.
  2. A second flavor: exactly k = at most k minus at most k - 1.
  3. Where distinct counting shows up in production (unique visitors, active users).
  4. What breaks it: zero-count keys, recounting, counting "exactly" directly, int counts.

The approach

Java

int[] distinctCountInWindows(int[] nums, int k) {
    int[] result = new int[nums.length - k + 1];
    Map<Integer, Integer> freq = new HashMap<>();
    for (int i = 0; i < nums.length; i++) {
        freq.merge(nums[i], 1, Integer::sum);                           // value enters on the right
        if (i >= k && freq.merge(nums[i - k], -1, Integer::sum) == 0) {
            freq.remove(nums[i - k]);                                   // value fully left: drop the key
        }
        if (i >= k - 1) result[i - k + 1] = freq.size();                // size = distinct values now
    }
    return result;
}

Python

def distinct_count_in_windows(nums: list[int], k: int) -> list[int]:
    result = []
    freq = {}
    for i, x in enumerate(nums):
        freq[x] = freq.get(x, 0) + 1                   # value enters on the right
        if i >= k:
            out = nums[i - k]
            freq[out] -= 1
            if freq[out] == 0:
                del freq[out]                          # value fully left: drop the key
        if i >= k - 1:
            result.append(len(freq))                   # size = distinct values now
    return result

The map holds exactly the values inside the window with their counts, so its size is the answer. That only works if a key is removed the moment its count reaches 0; a zero-count key left behind still counts toward size(). In Python, a Counter is the easy way to get this wrong: c[x] -= 1 leaves a key at 0 and len(c) still counts it, so del the key explicitly. Every value enters and leaves once, so the whole scan is O(n) with O(k) space. When values are small integers (here at most 10^5), a count array and a counter that goes up when a count becomes 1 and down when it becomes 0 do the same job without hashing.

A second flavor: exactly k = at most k minus at most k - 1

"How many subarrays contain exactly k distinct values?" A single shrinking window doesn't work: for one right end, several different starts can have exactly k distinct values. On [1, 2, 1, 2, 3] with k = 2 the answer is 7, and a single window that counts one subarray per right end finds only 4. "At most k" is easy to count, though, and exactly k is a difference of two of them:

Java

long subarraysWithKDistinct(int[] nums, int k) {
    return atMost(nums, k) - atMost(nums, k - 1);
}

long atMost(int[] nums, int k) {                        // subarrays with at most k distinct values
    Map<Integer, Integer> freq = new HashMap<>();
    long count = 0;
    for (int left = 0, right = 0; right < nums.length; right++) {
        freq.merge(nums[right], 1, Integer::sum);
        while (freq.size() > k) {                       // too many distinct: shrink from the left
            if (freq.merge(nums[left], -1, Integer::sum) == 0) freq.remove(nums[left]);
            left++;
        }
        count += right - left + 1;                      // every start from left to right is valid
    }
    return count;
}

Python

def subarrays_with_k_distinct(nums: list[int], k: int) -> int:
    return at_most(nums, k) - at_most(nums, k - 1)


def at_most(nums: list[int], k: int) -> int:           # subarrays with at most k distinct values
    freq = {}
    count = left = 0
    for right, x in enumerate(nums):
        freq[x] = freq.get(x, 0) + 1
        while len(freq) > k:                           # too many distinct: shrink from the left
            freq[nums[left]] -= 1
            if freq[nums[left]] == 0:
                del freq[nums[left]]
            left += 1
        count += right - left + 1                      # every start from left to right is valid
    return count

For each right end, the shortest valid window starts at left, and every longer window that still ends at right and starts after left has at most k distinct values too - right - left + 1 of them. The count can reach n(n + 1)/2, about 5 * 10^9 for n = 10^5, so it's a long.

Where you'll see it

"Distinct users active in the last five minutes" and "distinct IPs hitting this endpoint" are rolling distinct counts. At large scale they're estimated rather than counted exactly: Redis's HyperLogLog (PFADD/PFCOUNT) estimates a distinct count with 0.81% standard error in at most 12 KB, however many values it sees.

Which tool for which job?

You needUse
Distinct count in every fixed windowa frequency map, removing keys at zero
Subarrays with at most k distincta variable window, adding right - left + 1 per right end
Subarrays with exactly k distinctat most k minus at most k - 1, or two left pointers
Distinct count in arbitrary ranges, many queriesoffline, sorted by right end, with a Fenwick tree
Approximate distinct count, huge dataHyperLogLog

Common mistakes

MistakeWhat happensDo this instead
Leaving zero-count keyssize() / len() overcountsremove a key at zero (in Python, +counter drops them)
Recounting each windowO(n × k)update as values enter and leave
One shrinking window for "exactly k"misses later starts (4 instead of 7)the at-most difference
An int subarray count5,000,050,000 wraps to 705,082,704long

Check yourself

Try to answer before opening each one - recalling beats re-reading.

1. Why must a key be removed when its count reaches 0?

Show answer

The distinct count is read as the map's size. A key with count 0 is no longer in the window but still counts toward size(), so every later answer is too high.

2. Why does exactly(k) equal atMost(k) - atMost(k - 1)?

Show answer

Every subarray with at most k distinct values has either exactly k or at most k - 1. Subtracting the second group from the first leaves exactly the subarrays with k distinct values.

3. In atMost, why does each right end add right - left + 1?

Show answer

After shrinking, [left, right] is the longest window ending at right with at most k distinct values. Any start between left and right gives a shorter window inside it, which has at most k distinct too - right - left + 1 subarrays in all.

Key takeaways

  • Slide the window: add the incoming count, subtract the outgoing one, delete keys that reach 0.
  • The map's size is the window's distinct count - O(n) overall.
  • "Exactly k" = atMost(k) - atMost(k - 1); count right - left + 1 per right end.
  • Subarray counts need a long.
  • At scale, distinct counts are estimated (HyperLogLog).

Quick revise

The whole skill on one page, for the night before an interview.

This page puts the lesson and the deep dive together. Skim it before practice or an interview, and go back to the full lesson for anything you don't recognize.

Pick the tool

You needUse
Distinct per fixed windowfrequency map, remove at zero
At most k distinctvariable window
Exactly kat-most difference / two left pointers
Arbitrary rangesoffline plus Fenwick
Huge data, approximateHyperLogLog

Snippets worth knowing by heart

Java

long count = 0;
for (int left = 0, right = 0; right < n; right++) {
    freq.merge(a[right], 1, Integer::sum);
    while (freq.size() > k) { if (freq.merge(a[left], -1, Integer::sum) == 0) freq.remove(a[left]); left++; }
    count += right - left + 1;                        // long: up to n(n+1)/2
}

Python

freq[x] -= 1
if freq[x] == 0:
    del freq[x]                          # or len(+counter) - zero counts still count in len()

The traps

  • Zero-count keys: size() counts values that left the window. Remove a key when it hits zero.
  • Recounting: rebuilding each window is O(n x k). Update as values enter and leave.
  • "Exactly" with one window: a shrinking window misses later starts. Compute at-most(k) minus at-most(k - 1).
  • int counts: subarray counts pass 2^31 on large input. Use long.

Staff-level points, one line each

  • One-pass exactly-k with two left pointers matched the at-most difference on 500 arrays.
  • Offline range distinct counts with a Fenwick tree matched brute force on 2,000 queries.
  • HyperLogLog with 1,024 registers was 1.1% off on 311,020 distinct values; merging by register max is exact.
  • n = 100,000: 5,000,050,000 subarrays became 705,082,704 in an int.
  • Counter("aab") after one decrement still has len 2; len(+c) is 1.

Say it in an interview

  • "Only the entering and leaving values change, so I update a frequency map."
  • "Exactly k is at most k minus at most k - 1."
  • "For arbitrary ranges I'd go offline with a Fenwick tree."
  • "At scale, HyperLogLog gives about 1-3% error in kilobytes."

Practice

This skill comes with 9 multiple-choice questions that tell you which misconception a wrong answer reveals and 2 graded problems. Your answers are graded and your mastery is tracked, so you'll know when the skill holds up. Running code and saving progress need a free account.

Practice Sliding Window Distinct Count free

Distinct Numbers in Each Subarray

MediumProCoding problemJava · Python

Given an array of n integers and a window size k, for every contiguous subarray (window) of size k, find the count of distinct elements in it. There are exactly n - k + 1 such windows.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Subarrays With Exactly K Distinct

HardProCoding problemJava · Python

Count the runs of consecutive elements in nums that contain exactly k different values.

The full statement, the examples and the hidden test suite are part of AI Proof Engineers Pro.

Practice Sliding Window Distinct Count free

No card needed. Lessons, practice questions and easy problems are free forever.

Free plan, no card · 7-day Pro trial · Cancel anytime