Count Substrings Exactly K Distinct

Count Substrings Exactly K Distinct

Return the number of non-empty substrings of s that contain exactly k distinct characters.

countExactlyKDistinct(s, k)

Input format: s | k.

Examples

s="abc", k=2    // → 2   (ab, bc)
s="aabb", k=2   // → 4   (aab, abb, ab, ... actually: ab, aab, abb, aabb)
s="abc", k=0    // → 0

Walkthrough

Thinking it through

You need substrings with exactly k distinct characters. "Exactly" doesn't have the same clean monotonic structure that made the "at most k" window trick work: valid and invalid windows for a fixed ending point aren't a single contiguous range, so a plain two-pointer scan can't capture it in one pass.

The reframe: turn "exactly" into a difference of two "at most" problems

Substrings with at most k distinct characters, minus substrings with at most k-1 distinct, leaves exactly those with precisely k — every "at most k" substring either has exactly k or fewer, and the second count captures the "fewer" group exactly.

Why this is better than trying to solve "exactly k" directly

A single-window "exactly k" attempt would need two failure modes at once (too many distinct, and after shrinking, too few) that don't combine into one monotonic window. "At most k" is a problem you already know how to solve cleanly: growing the window only ever increases or holds the distinct count, so there's always a clean valid/invalid boundary.

Building the approach

  1. Implement a helper counting substrings with at most m distinct characters, using the standard window: expand, shrink from the left when the count exceeds m, add right - left + 1 each step.
  2. Call it with m = k and m = k - 1.
  3. Subtract: atMost(k) - atMost(k-1).
  4. Treat k - 1 negative (when k is 0) as zero substrings, since no non-empty substring can have negative distinct characters.

Why this is efficient

Each call is a standard O(n) sliding-window scan, so two calls together are still O(n) overall. "Exactly X" = "at most X" minus "at most X-1" is a broadly useful decomposition whenever a target condition lacks a clean monotonic window on its own, but a cumulative version of it does.

main.py
Console
Press Run to execute your code, or Submit to test and complete this problem.