## Sliding Window Two pointers **plus incremental state** between them. Never recompute the window from scratch. --- ## Recognition - "Longest / shortest **contiguous** subarray or substring such that…" - Constraint is monotone: growing the window can only make it worse - Fixed window size k → same idea, both ends move together --- ## Template — variable window (LC 3) ```js function lengthOfLongestSubstring(s) { const seen = new Set(); let best = 0; let l = 0; for (let r = 0; r < s.length; r++) { while (seen.has(s[r])) { // shrink until valid seen.delete(s[l]); l++; } seen.add(s[r]); best = Math.max(best, r - l + 1); } return best; } ``` Grow with `r`, shrink with `l` only while invalid. --- ## Why it's O(n) - `r` moves n times, `l` moves at most n times - Each element enters and leaves the window **once** - The inner `while` is amortized, not nested work --- ## Pitfalls - State must update in O(1) on both add and remove - "Shortest window" flips the loop: shrink while **valid**, record before breaking - Doesn't apply when the constraint isn't monotone (mixed signs → prefix sum) --- ## Recall - LC 121 Best Time to Buy/Sell: what is the "window state"? (min so far) - LC 76 Minimum Window: what makes a window "valid"? - When must you fall back to prefix sums instead? --- ## Drill - 121 Best Time to Buy and Sell Stock - 3 Longest Substring Without Repeating Characters - 424 Longest Repeating Character Replacement - 567 Permutation in String · 76 Minimum Window Substring - 209 Minimum Size Subarray Sum