Sliding window — longest/shortest contiguous run
📖 Walk me through it — plain English
The job: given a string of characters, find the length of the longest stretch of back-to-back characters that has no repeats. "Back-to-back" (the word "contiguous") means the characters must sit next to each other — no skipping around. For "abcabcbb" the answer is 3, because "abc" is the longest run where every letter is different.
A "sliding window" is just a pair of markers — a left edge and a right edge — that fences off the slice of the string we are currently looking at. The plan: keep pushing the right edge forward one character at a time to grow the window. The moment that newest character creates a duplicate inside the fence, we yank the left edge forward just far enough to drop the older copy, so the window holds only distinct characters again. After every move we record the window's length if it beats our best so far.
The clever part is knowing how far to jump left. We keep a little notebook called last that remembers, for each character, the index where we most recently saw it. When the new character ch is one we have seen before and that earlier sighting is still inside our fence (last[ch] >= left), we slide left to one past that old position — instantly skipping the duplicate without checking every cell in between.
Analogy: you are reading a row of name tags, gathering the longest run of all-different names. You hold a sticky note at the start of your current run. When you hit a name you already have, you don't back up one step at a time — you remember exactly where that name first appeared and jump your sticky note to right after it. Everything from there to now is fresh again.
Why it's fast: the right edge visits each character once, and the left edge only ever moves forward — it never backtracks. So across the whole run each marker travels the length of the string at most once. That's O(n) time (work grows in a straight line with the string length n), and the last notebook holds at most one entry per distinct character, so O(k) extra memory where k is the alphabet size. The reason it's correct: by jumping left to one past any duplicate, we guarantee the window between the two markers is always free of repeats, so every length we measure is a valid candidate — and we keep the biggest one.
Expand right; shrink left while the invariant is broken. Update answer at the right moment.
- "Longest substring with property"
- "Smallest subarray with sum ≥ k"
- "At most K of something"
- Fixed window of size k
- Shrink while invariant broken, not if
- Update answer after restore, not during break
- Window length = j - i + 1
- Initialize counts before the loop
def length_of_longest(s):
last = {}; left = best = 0
for right, ch in enumerate(s):
if ch in last and last[ch] >= left:
left = last[ch] + 1
last[ch] = right
best = max(best, right - left + 1)
return best
function lengthOfLongest(s: string): number {
const last = new Map<string,number>();
let left=0, best=0;
for (let right=0; right<s.length; right++) {
const ch = s[right];
if (last.has(ch) && last.get(ch)! >= left) left = last.get(ch)! + 1;
last.set(ch, right);
best = Math.max(best, right - left + 1);
}
return best;
}
Write the fixed-size window yourself — add the entering element, drop the leaving one, never re-sum. Live tests below: