A sliding window walks a string of uppercase letters one character at a time, playing Longest Repeating Character Replacement (LC 424): the longest run of a single letter you can make with at most k replacements. Under the window sit three bars drawn in the same units as the tiles — length, max count (the most frequent letter inside), and length − max against a fence at k — so the invariant every valid window satisfies is a picture you can check by eye instead of a line you take on faith. The right edge takes one character; if the third bar crosses the fence, the left edge steps once, and the window slides instead of shrinking.
The point of the piece is the second bar. In the default mode the max count is a high-water mark: raised when an incoming letter’s count beats it, never lowered when the left edge lets those letters go. So it goes stale — the bar shows the tracked value in hatched purple beyond the live one, the window can be flagged not valid — and the answer still comes out right. The reason is written on screen when it happens: the window’s length was earned by a window that really was valid, and it can only grow again once a live count makes a longer window genuinely valid. Wrong-looking but sound, which is exactly why the O(n) solution is hard to trust on a whiteboard.
Switch the max count to recomputed · shrink until valid to watch the textbook-honest version on the same string: the window is valid after every step, it can shrink, and the readout at the end shows both bookkeepings agreeing on the answer. Type your own string, set k, step through, or let it run.
Reuse
src/window.js is a framework-free ES module:
makeString(n, { alphabet, stickiness, rand })— a random uppercase string biased toward runs, so windows have something to hold on to.cleanString(s, max)— uppercase letters only, capped, for user input.longestRepeatingReplacement(s, k)— the plain answer, high-water mark and single slide, as you would write it in an interview.simulate(s, k, { highWater })— one frame per right-edge step with the window, live counts, tracked max, live max, whether the tracked max is stale, whether the window is currently valid, and the best so far. WithhighWater: falseit runs the honest recompute-and-shrink-until-valid variant instead. Same trace-per-tick shape asrho,siftandastar-grid.
No rendering or timers in the module; the canvas demo is reference code.
Gotchas
- In high-water mode the window is never invalid while the max is live: the failed check overshoots k by exactly one, so one slide restores it. Invalid windows only ever appear under a stale maximum — if you see one with the hatched bar absent, something is wrong.
- k = 0 is legal and degenerates to “longest run of one letter”; k ≥ length makes the whole string the answer after the last step. Both are worth one look each.
- The demo bundles its own copy of
window.js(self-contained by contract); re-copy after editingsrc/.