</> MAANG.io
coding interview · 301

Advanced

Advanced coding interview preparation covering complex algorithms, system design, and optimization techniques for senior-level positions.

0/95 solved 0% complete

Variable Window

What is this?

Three problems that look like the same grow-and-shrink loop and are not. The first genuinely is one, wrapped in a streaming interface. The second breaks the technique's central assumption and needs the free variable removed before a window applies at all. The third contains negative numbers, which makes a window not merely awkward but wrong.

This chapter is where you learn to check the assumption instead of reaching for the template.

flowchart TD A["is the constraint monotonic in window size?"] --> B{"test it"} B -->|"yes — 'at most k'"| C["standard grow-and-shrink"] B -->|"no — 'at least k each'"| D["fix the target distinct count
run the window once per value"] B -->|"negatives present"| E["a window cannot work"] E --> F["monotonic deque over prefix sums"]

💡 Fun fact: Shortest Subarray with Sum at Least K is the same sentence as an easy sliding-window problem with one word changed — the values may be negative. That single change is fatal: a growing window's sum is no longer non-decreasing, so "shrink while the sum is too large" can discard the answer. The correct solution keeps a deque of prefix-sum indices in increasing order, popping from the front when a candidate qualifies and from the back when a new prefix is smaller than an older one — because an older, larger prefix can never beat a newer, smaller one for any future endpoint.

🔓 The 3 problems in this chapter are free. Sign in with Google or Microsoft to start solving.


The one-line idea: the template requires the constraint to move one way as the window grows. When it does not, either remove the free variable so it does, or drop the window for a structure that can discard dominated candidates.


1. Why "at least k" breaks it

For Longest Substring with At Least K Repeating Characters, consider "aaabb" with k = 3. The window "aab" fails — b appears once. Extending it to "aabb" still fails. Extending to "aaabb" succeeds. An invalid window became valid by growing, which is precisely what the shrink rule assumes cannot happen.

Two repairs, both worth knowing:

Fix the target. Run the ordinary at-most-t-distinct window for each t from 1 to 26, and inside each run additionally require that every character present appears at least k times. With t fixed the constraint is monotonic again, so the template is valid. Total cost O(26n).

Divide and conquer. Any character appearing fewer than k times in the whole string can never be part of an answer, so split the string on it and recurse into the pieces. When no such character exists, the whole string qualifies.

2. Why negatives break it, and the deque

from collections import deque
prefix = [0]
for x in nums: prefix.append(prefix[-1] + x)

dq, best = deque(), len(nums) + 1
for i, p in enumerate(prefix):
    while dq and p - prefix[dq[0]] >= k:      # this endpoint qualifies
        best = min(best, i - dq.popleft())
    while dq and prefix[dq[-1]] >= p:         # an older, larger prefix is dominated
        dq.pop()
    dq.append(i)
return best if best <= len(nums) else -1

Both pops are essential and they do different jobs. The front pop harvests answers — once a window qualifies, no longer window starting there can be better, so it is removed permanently. The back pop discards prefixes that are larger than a newer one: a bigger prefix arriving earlier can never produce a shorter qualifying window than a smaller prefix arriving later.

3. The streaming variant

At Most K Distinct Characters in a Stream is the honest window of the three, with the twist that characters arrive one at a time and you must answer after each. The state is the usual count map plus a live-key count, and the discipline is the same: delete a key the moment its count reaches zero, or the distinct count silently drifts.


4. A 30-second worked example (the deque)

nums = [2, -1, 2], k = 3. The answer is 3 — the whole array.

prefix:  [0, 2, 1, 3]
          i=0  i=1  i=2  i=3

i=0 p=0   dq empty         → push 0            dq=[0]
i=1 p=2   2-0=2 < 3        → no harvest
          prefix[0]=0 < 2  → no domination pop → push 1   dq=[0,1]
i=2 p=1   1-0=1 < 3        → no harvest
          prefix[1]=2 >= 1 → pop 1 (dominated) → push 2   dq=[0,2]
i=3 p=3   3-prefix[0]=3 >= 3 → harvest: 3-0 = 3 ✓  popleft
          3-prefix[2]=2 < 3  → stop
                                          answer = 3

Note the domination pop at i=2: prefix 2 was larger than the newer prefix 1, so it could never yield a shorter window and was discarded. A plain sliding window, having shrunk past the -1, would never have found this.


5. Where you'll actually meet this

  • Monitoring and alerting. Shortest burst whose total crosses a threshold, over metrics that are deviations and therefore signed.
  • Financial analysis. Shortest period of cumulative gain over a target, where daily returns are negative as often as positive.
  • Streaming text processing. Bounded-vocabulary constraints over an unbounded input, with memory fixed in advance.
  • Quality control. Shortest run meeting a cumulative tolerance where individual measurements can be under or over.

6. Problems in this chapter

▶ At Most K Distinct Characters in a Stream

Report, after each arriving character, whether the recent window holds at most k distinct values. The genuine template, with a streaming interface.
Pattern: grow-and-shrink with a count map. Target: O(1) amortised per character, O(k) space.

▶ Longest Substring with At Least K Repeating Characters

Longest substring in which every present character appears at least k times. Not monotonic — fix the distinct count and run the window 26 times, or split on disqualifying characters and recurse.
Pattern: fixed-target windows, or divide and conquer. Target: O(26n), or O(n log n) average.

▶ Shortest Subarray with Sum at Least K

Shortest subarray summing to at least k, with negatives allowed. A monotonic deque over prefix sums; a sliding window is provably wrong here.
Pattern: monotonic deque over prefix sums. Target: O(n) time, O(n) space.


7. Common pitfalls 🚫

  • Using a sliding window with negatives. It looks right, passes positive-only tests, and is wrong. Say why.
  • Assuming "at least" is monotonic. Construct the two-window counter-example before trusting the template.
  • Only popping the front of the deque. Without the domination pop the deque is not monotonic and the answer is not minimal.
  • Popping the front without harvesting. Once a window qualifies, record it — that index will never be useful again.
  • Off-by-one in the prefix array. prefix has n + 1 entries and the window length is a difference of indices into prefix, not into nums.
  • Not deleting zero-count keys in the streaming problem, which corrupts the distinct count.
  • Recursing on the whole string in the divide-and-conquer version when no character disqualifies — that is the base case, not a recursion.

8. Key takeaways

  1. Test monotonicity first. It is the assumption the entire technique rests on.
  2. "At most" is monotonic. "At least" is not. One word changes the algorithm.
  3. Remove the free variable — fixing the distinct count restores the template's validity.
  4. Negatives mean a deque over prefix sums, not a window.
  5. Two pops, two purposes: front harvests answers, back discards dominated prefixes.
  6. Smaller and newer beats larger and older — the domination rule, and the reason the deque stays monotonic.
  7. Why interviewers like it: these problems are dressed to look routine. The signal is entirely in whether you check the assumption before writing the loop.

Order: At Most K Distinct Characters in a Stream → Longest Substring with At Least K Repeating Characters → Shortest Subarray with Sum at Least K.

At Most K Distinct Characters in a Stream

Core idea: This is the streaming cousin of Longest Substring with At Most K Distinct Characters. There, the whole string sits in memory and you sweep a two-pointer window over it. Here the characters arrive one at a time — you can't see the future and you can't re-scan the past. So you maintain the at-most-k-distinct invariant incrementally: keep a rolling count map char -> count plus a distinct counter (the number of keys with count > 0). On each arriving char, bump its count; if it was at zero, the distinct counter ticks up. When you need a streaming window that never exceeds k distinct, shrink from the left — replay the oldest still-buffered characters, decrementing their counts, and tick the distinct counter down whenever a count hits zero — until distinct <= k again. Each character is added once and removed at most once → O(1) amortized per char.


The problem, rephrased

You're consuming a never-ending feed of characters — keystrokes, log tokens, sensor symbols — and they show up one at a time. You can't load the whole feed into memory and you can't rewind it. Two flavours of question your stream object must answer online:

  1. Predicate flavour: after feeding each new character, can you instantly answer "does the current window hold at most k distinct characters?"
  2. Longest-window flavour: maintain, as the stream advances, the length of the longest suffix window that still contains at most k distinct characters — i.e. the right edge is always "now," and you shrink the left edge just enough to stay legal.

The mechanism is the same for both: a rolling count map and a distinct counter that you update on every arrival, plus a left-side eviction loop when a streaming window must be bounded.

Stream so far k Longest legal suffix window Length
e c e 2 e c e 3
e c e b 2 c e b 3
e c e b a 2 b a 2
a a b b c c 1 c c (current suffix) 2

This is the interviewer's classic follow-up: "Now suppose the string is given as a stream — you don't get the whole thing up front." The offline two-pointer answer assumed random access to a fixed string; the streaming framing takes that away and asks you to hold the invariant with state alone.


Sign in to continue reading

The rest of this lesson is available with a free account. Signing in with Google or Microsoft is free.

Sign in to read the full lesson

Sign in to MAANG.io

Use your Google or Microsoft account — no password to remember.

Continue with Google Continue with Microsoft

Please accept the terms above to continue.