You cannot slide a window for exactly k distinct values. The predicate is not
monotone: at distinct == k, extending to the right can jump to
k+1, and there is no shrink rule that lands you back — shrinking from the
left can drop you to k−1 in the same move. But
distinct ≤ s is monotone: once a window holds too many values, removing
from the left only ever removes values, and they never come back on their own. So
atMost(s) is windowable, and
exactly(k) = atMost(k) − atMost(k−1). Run the same O(n) window
twice and subtract. Inside each pass a valid window [l..r] banks
r − l + 1 subarrays at once — one per suffix ending at
r. The trap lives in the map: a count that falls to zero must have its
key deleted, or size() never falls and the loop never ends.
exactly(k) = atMost(k) − atMost(k−1) — it works only because distinct ≤ s is monotone and distinct == k is not. Delete the key when its count hits zero, or size() lies.