Loading content...
Imagine looking at a string through a narrow window that can slide left to right. At any moment, you can only see the characters inside the window. As the window slides, some characters leave your view while new ones enter. This is the sliding window technique—a powerful pattern for problems involving contiguous substrings.
Sliding window builds directly on co-moving two-pointer techniques, but with a specific focus: maintaining and processing a contiguous range (or "window") that moves through the string. The left edge of the window contracts it; the right edge expands it. What happens inside the window determines the solution.
This page develops your intuition for sliding window problems on strings. We'll focus on understanding when this technique applies and how to think about window mechanics, preparing you for rigorous algorithm study later.
By the end of this page, you will understand the sliding window mental model, distinguish between fixed-size and variable-size windows, recognize problem characteristics that signal sliding window applicability, and grasp how window state is maintained efficiently using incremental updates.
A sliding window is a contiguous range within a string (or array) defined by two pointers—left and right—that "slide" through the data.
The Window:
The Sliding Action:
As we process the string:
The art of sliding window problems is knowing when to expand and when to contract.
Visual Representation:
String: a b c d e f g h i j
0 1 2 3 4 5 6 7 8 9
Window at time T1: [L=2, R=5)
a b [c d e] f g h i j
^^^^^
Window contains "cde"
Window at time T2 (after sliding): [L=4, R=7)
a b c d [e f g] h i j
^^^^^
Window contains "efg"
The window has "slid" from one contiguous substring to another. The characters that left (c, d) are no longer considered; the characters that entered (f, g) are now included.
Sliding window is a specialized form of co-moving two pointers. Both pointers move in the same direction (left to right), but they maintain a relationship: they define the boundaries of a meaningful range. The "window state" encapsulates what we care about within that range.
The simplest form of sliding window has a fixed size. The window is always exactly k characters wide, and it slides through the string examining every contiguous substring of length k.
Fixed Window Mechanics:
Example: Find All Anagrams of Pattern P in Text T
Given text T and pattern P of length k, find all starting positions where T contains an anagram of P.
Approach:
12345678910111213141516171819202122232425262728293031323334
FindAnagramPositions(T, P): k = length(P) n = length(T) IF k > n: RETURN empty list // Build frequency map for pattern targetFreq = buildFrequency(P) // Build frequency map for initial window windowFreq = buildFrequency(T[0:k]) result = empty list // Check initial window IF windowFreq == targetFreq: result.add(0) // Slide the window FOR i = k TO n - 1: // Add character entering window (at position i) windowFreq[T[i]] += 1 // Remove character leaving window (at position i - k) windowFreq[T[i - k]] -= 1 IF windowFreq[T[i - k]] == 0: remove T[i - k] from windowFreq // Check if current window matches IF windowFreq == targetFreq: result.add(i - k + 1) // Starting position of this window RETURN resultWhy This Works:
Without Sliding Window:
A naive approach would rebuild the frequency map for each window from scratch:
Sliding window achieves O(n) by reusing work—the key insight is that adjacent windows share k-1 characters.
Fixed-size sliding windows exploit massive overlap between consecutive windows. Window [i, i+k) and window [i+1, i+k+1) share k-1 characters. Recomputing from scratch wastes this overlap. Incremental updates (add one, remove one) leverage it for O(1) transitions.
More interesting problems involve variable-size windows—windows that grow and shrink based on conditions. These problems ask for the longest, shortest, or optimal window satisfying some constraint.
Variable Window Mechanics:
The Expand-Contract Dance:
FOR R = 0 TO n - 1:
// Expand: include T[R] in window
updateWindowState(add T[R])
WHILE window violates condition:
// Contract: remove T[L] from window
updateWindowState(remove T[L])
L += 1
// Window [L, R] now satisfies condition
// Update answer if this window is optimal
Example: Longest Substring Without Repeating Characters
Find the length of the longest substring with all unique characters.
Condition: Window is valid when all characters in it are unique (no duplicates).
Expand: Always move R to add the next character.
Contract: If the new character creates a duplicate, move L until the duplicate is removed.
Track: The maximum window size (R - L + 1) seen when window is valid.
Trace for "abcabcbb":
| R | Char | Window After Add | Valid? | Contract? | Window After Contract | Max |
|---|---|---|---|---|---|---|
| 0 | a | [a] | Yes | No | [a] | 1 |
| 1 | b | [a,b] | Yes | No | [a,b] | 2 |
| 2 | c | [a,b,c] | Yes | No | [a,b,c] | 3 |
| 3 | a | [a,b,c,a] | No (dup a) | Yes | [b,c,a] | 3 |
| 4 | b | [b,c,a,b] | No (dup b) | Yes | [c,a,b] | 3 |
| 5 | c | [c,a,b,c] | No (dup c) | Yes | [a,b,c] | 3 |
| 6 | b | [a,b,c,b] | No (dup b) | Yes | [c,b] | 3 |
| 7 | b | [c,b,b] | No (dup b) | Yes | [b] | 3 |
Answer: 3 (substring "abc")
Variable-size windows work because of monotonicity: if a window is invalid, making it larger won't fix it (for problems like 'all unique'). Conversely, making it smaller might restore validity. This monotonicity justifies the expand-until-invalid, contract-until-valid strategy.
The efficiency of sliding window algorithms depends on how we maintain window state—the information needed to evaluate conditions and update answers.
Common State Types:
1. Character Frequency Map
Track how many times each character appears in the current window.
Operations:
freq[c] += 1freq[c] -= 1Use cases: Anagram finding, character coverage, duplicate detection.
2. Sum/Aggregate
For problems involving character values (e.g., sum of ASCII values).
Operations:
sum += value(c)sum -= value(c)Use cases: Substring sums, average calculations.
3. Distinct Count
Track the number of distinct characters in the window.
Operations:
if freq[c] == 0: distinctCount += 1; freq[c] += 1freq[c] -= 1; if freq[c] == 0: distinctCount -= 1Use cases: "At most k distinct characters," "exactly k distinct characters."
4. Validity Counter
For problems like "window contains all characters of pattern," track how many distinct required characters are fully covered.
Operations:
windowFreq[c] == targetFreq[c]: formed += 1windowFreq[c] < targetFreq[c]: formed -= 1formed == requiredDistinctUse cases: Minimum window substring, permutation in string.
The hardest part of sliding window problems is often designing the right state. Ask: What do I need to know about the window to check the condition? Can I update this information in O(1) when adding/removing one character? If yes, sliding window likely works. If state requires O(k) to update, reconsider your approach.
| State Type | Add Operation | Remove Operation | Space |
|---|---|---|---|
| Frequency Map | freq[c]++ | freq[c]-- | O(|Σ|) |
| Sum/Aggregate | sum += val(c) | sum -= val(c) | O(1) |
| Distinct Count | if 0→1: count++ | if 1→0: count-- | O(|Σ|) |
| Coverage Counter | if match: formed++ | if unmatch: formed-- | O(|Σ|) |
| Min/Max (complex) | Use monotonic deque | Pop from deque | O(k) |
Recognizing when sliding window applies is a crucial skill. Look for these signals:
Signal 1: Contiguous Substring/Subarray Required
Keywords: "contiguous," "substring," "subarray," "consecutive"
If the problem asks about contiguous portions of the input, sliding window might apply. (Contrast with "subsequence," which is non-contiguous and typically needs different techniques.)
Signal 2: Optimization Over All Windows
Keywords: "longest," "shortest," "minimum," "maximum," "find the length of"
Problems asking for the best (longest/shortest) contiguous range satisfying a condition are prime sliding window candidates.
Signal 3: Counting Windows
Keywords: "count how many subarrays," "find all substrings that"
Counting all windows satisfying a condition often uses sliding window with careful counting logic.
Signal 4: Condition on Window Content
Keywords: "contains all," "at most k," "exactly k," "without repeating," "with equal number of"
Conditions about what the window must or must not contain are sliding window indicators. The condition determines when to expand vs. contract.
Signal 5: Fixed-Size Pattern Matching
Keywords: "of length k," "substrings of size k," "every window of size"
Explicit window size suggests fixed-size sliding window. Simple to implement once recognized.
Sliding window fails when: (1) The problem involves non-contiguous elements (use subsequence techniques instead), (2) The condition isn't monotonic (expanding might fix a problem), (3) You need to track relationships that can't be updated incrementally. Force-fitting sliding window to these problems leads to incorrect solutions.
One of the most important sliding window problems is "Minimum Window Substring": find the shortest substring of S that contains all characters of T. This problem illustrates sophisticated window management.
The Challenge:
The Strategy:
The key insight: we maintain both when the window is valid and track the minimum among all valid windows.
1234567891011121314151617181920212223242526272829303132333435363738394041424344
MinWindowSubstring(S, T): IF length(T) > length(S): RETURN "" // Target frequencies we need to satisfy targetFreq = buildFrequency(T) required = number of distinct chars in T // Window state windowFreq = empty frequency map formed = 0 // How many distinct chars are fully covered // Answer tracking minLength = infinity minStart = 0 L = 0 FOR R = 0 TO length(S) - 1: // Expand: add S[R] to window c = S[R] windowFreq[c] += 1 // Check if this char is now fully covered IF c in targetFreq AND windowFreq[c] == targetFreq[c]: formed += 1 // Contract while window is valid WHILE formed == required: // Current window is valid; check if it's minimal IF R - L + 1 < minLength: minLength = R - L + 1 minStart = L // Remove S[L] from window leftChar = S[L] windowFreq[leftChar] -= 1 IF leftChar in targetFreq AND windowFreq[leftChar] < targetFreq[leftChar]: formed -= 1 L += 1 IF minLength == infinity: RETURN "" RETURN S[minStart : minStart + minLength]Why This Pattern Is Important:
This algorithm demonstrates:
required (static target) and formed (dynamic window state)formed == required means window contains all needed charactersOnce you master this pattern, many similar problems become straightforward variations.
Rather than comparing full frequency maps (O(|Σ|) per check), we track 'formed'—the count of fully satisfied character requirements. When formed equals the number of distinct characters needed, the window is valid. This O(1) validity check is what makes the algorithm efficient.
Sliding window algorithms are prone to specific bugs. Awareness prevents wasted debugging time.
Pitfall 1: Off-By-One in Window Size
For a window [L, R):
Choose one convention and stick to it. Mixing conventions causes bugs.
Pitfall 2: Forgetting to Remove Zero-Count Entries
When comparing frequency maps:
{a: 2, b: 0} should equal {a: 2} for most problemsPitfall 3: Incorrect Contraction
Pitfall 4: Not Updating Answer at the Right Time
For 'longest valid window': Update answer when window is valid before contracting. For 'shortest valid window': Update answer when window first becomes valid, then again after each contraction if still valid.
Before submitting, trace through: (1) a minimal valid case (pattern == text), (2) a case with no valid window, (3) a case where the optimal window is at the very end, (4) a case where the optimal window is the entire string. These catches most boundary bugs.
Sliding window algorithms achieve excellent time complexity through incremental updates.
Time Complexity: O(n)
Both fixed and variable-size sliding windows are O(n):
Fixed-size (window size k):
Variable-size:
The variable-size sliding window is O(n) despite the nested while loop because L never moves backward. Over the entire algorithm, L moves right at most n times total. This amortization across all iterations gives O(n), not O(n²).
| Variant | Time | Space | Notes |
|---|---|---|---|
| Fixed-size window | O(n) | O(k) or O(|Σ|) | k = window size |
| Variable-size window | O(n) | O(|Σ|) | State space typically O(|Σ|) |
| Naive recompute each window | O(n × k) | O(k) | For comparison |
| Brute force all substrings | O(n² × check) | O(1) to O(n) | Much slower |
Space Complexity: O(|Σ|) or O(k)
Space depends on window state:
Comparison with Alternatives:
| Approach | Time | Space | When to Use |
|---|---|---|---|
| Sliding window | O(n) | O( | Σ |
| Two pointers (non-window) | O(n) | O(1) | Pairs, not ranges |
| Hash all substrings | O(n² × hash) | O(n²) | Need to store all substrings |
| Suffix array + LCP | O(n log n) | O(n) | Complex substring queries |
Let's connect sliding window to the other patterns we've learned in this module:
Sliding Window + Frequency Counting
Many sliding window problems use frequency counting as the window state:
The two techniques are deeply complementary.
Sliding Window + Prefix/Suffix
Fixed windows of size k are essentially adjacent prefixes of suffixes:
Sliding Window + Two Pointers
Sliding window IS two pointers with a specific structure:
The Pattern Evolution:
1. Single Index Traversal
↓
2. Two Pointers (generic)
↓
3. Sliding Window (specialized two-pointer for ranges)
↓
4. Sliding Window + Frequency State
↓
5. Advanced Structures (monotonic deques, etc.)
Each level builds on the previous. Mastering sliding window prepares you for more advanced techniques like:
All these patterns—frequency counting, prefix/suffix analysis, two pointers, sliding window—are different lenses for viewing string problems. Expert problem solvers rapidly switch between lenses, combining them as needed. The goal isn't to memorize templates but to internalize the core ideas deeply enough to combine them fluidly.
We've built intuition for sliding window—one of the most powerful techniques for string (and array) problems involving contiguous ranges. Let's consolidate the key insights:
This module has equipped you with four fundamental patterns for string problem-solving: character frequency counting (composition view), prefixes/suffixes/substrings (structure view), two-pointer techniques (pair processing), and sliding window (range processing). These patterns appear throughout algorithm problems and form the foundation for advanced string algorithms like KMP, Rabin-Karp, suffix arrays, and more.
You now understand sliding window at an intuitive level—what it is, when it applies, how to maintain window state, and the key patterns like minimum window substring. Combined with the other techniques from this module, you have a powerful toolkit for tackling string problems. Practice these patterns on real problems to solidify your intuition.