All articles
Article

Sliding Window Hits 22% of FAANG Screens. Master It Fast.

Sliding window solves a specific class of problems: find the longest, shortest, maximum, or minimum contiguous subarray or substring satisfying some constraint…

14 min read

Sliding Window Hits 22% of FAANG Screens. Master It Fast.

Sliding window solves a specific class of problems: find the longest, shortest, maximum, or minimum contiguous subarray or substring satisfying some constraint. It shows up in roughly 22% of FAANG phone screens (per our internal Discord-tracked dataset of 800+ interview reports through Q1 2026). One of the three highest-value patterns to master alongside two pointers and BFS/DFS.

Two templates and 18 problems is the entire pattern. Master both templates and you'll recognize sliding window in under 90 seconds — and code the variable-size template from memory in under 8 minutes.

You get the two templates (fixed-size and variable-size), eighteen LeetCode walkthroughs ordered from easy to hard, and the decision tree for distinguishing sliding window from adjacent patterns (two pointers, prefix sum, monotonic deque). Work through this end-to-end and you'll recognize a sliding window problem in under 90 seconds and code the variable-size template from memory in under 8 minutes.

When to use sliding window

Four trigger phrases that should make you reach for sliding window:

  1. "longest substring [with property]" → variable-size
  2. "minimum window [containing X]" → variable-size
  3. "maximum sum subarray of size K" → fixed-size
  4. "subarray with at most K distinct [elements]" → variable-size

Three anti-triggers that look like sliding window but aren't:

  1. "any subarray sum equals K" → use prefix sum (sliding window fails on negative numbers)
  2. "find pair (i, j) with property" → use two pointers (no window state needed)
  3. "sliding window MAXIMUM" specifically (LC 239) → use monotonic deque, not vanilla sliding window

Decision tree, in plain language:

  • Is the answer a contiguous range (subarray or substring)? No? Not sliding window.
  • Are you tracking a window-state aggregate (sum, count, frequency map)? No? Probably two pointers.
  • Does the array contain negative numbers AND the constraint is sum-based? Yes? Use prefix sum, not sliding window.
  • Is the window size fixed (K) or variable (depends on a constraint)? Fixed: Template 1. Variable: Template 2.

TL;DR — the 2 templates

The entire pattern reduces to two templates. Memorize both.

Fixed-size template: when the window is exactly K elements. Maintain a running aggregate. Slide one position at a time. Update the aggregate by removing the leftmost element and adding the rightmost.

Variable-size template: when the window grows or shrinks based on a constraint. Two pointers (left, right) where right always advances. Left advances only when the window violates the constraint.

Both templates below in pseudo-code, then Python, then Java.

Fixed-size sliding window — the canonical template

The fixed-size case is simpler. The window is always exactly K elements. Initialize over the first K elements, then slide one position at a time, updating the aggregate in O(1) per slide.

Pseudo-code:

window_aggregate = aggregate(arr[0..K-1])
best = window_aggregate
for i from K to len(arr)-1:
    window_aggregate += arr[i] - arr[i-K]
    best = best_function(best, window_aggregate)
return best

Python:

def fixed_window(arr, K):
    window_sum = sum(arr[:K])
    best = window_sum
    for i in range(K, len(arr)):
        window_sum += arr[i] - arr[i - K]
        best = max(best, window_sum)
    return best

Java:

public int fixedWindow(int[] arr, int K) {
    int windowSum = 0;
    for (int i = 0; i < K; i++) windowSum += arr[i];
    int best = windowSum;
    for (int i = K; i < arr.length; i++) {
        windowSum += arr[i] - arr[i - K];
        best = Math.max(best, windowSum);
    }
    return best;
}

Common bug: off-by-one when initializing the first window. Fix: explicit pre-loop sum over first K elements instead of folding initialization into the main loop. Code clarity beats code cleverness in interview settings.

Variable-size sliding window — the workhorse template

The variable-size case is the workhorse. Most "longest", "shortest", "minimum window" problems use this template.

Pseudo-code:

left = 0
window_state = empty
best = initial_value
for right from 0 to len(arr)-1:
    add arr[right] to window_state
    while window_state violates constraint:
        remove arr[left] from window_state
        left += 1
    best = best_function(best, right - left + 1)
return best

Python (with frequency dict for char-count problems):

from collections import defaultdict

def longest_at_most_k_distinct(s, K):
    freq = defaultdict(int)
    left = 0
    best = 0
    for right in range(len(s)):
        freq[s[right]] += 1
        while len(freq) > K:
            freq[s[left]] -= 1
            if freq[s[left]] == 0:
                del freq[s[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

Common bug: forgetting to shrink left BEFORE recording the answer. Fix: shrink-then-record discipline. The best = max(...) line goes AFTER the inner while loop, never inside it.

Problem 1 — Maximum Average Subarray I (LC 643)

Difficulty: Easy. Pattern: fixed-size.

Walkthrough: window of K elements, find max running average. Trivial application of Template 1.

def findMaxAverage(nums, k):
    window_sum = sum(nums[:k])
    best = window_sum
    for i in range(k, len(nums)):
        window_sum += nums[i] - nums[i - k]
        best = max(best, window_sum)
    return best / k

Common mistake: accumulating into a running sum that overflows in Java when the array contains values near INT_MAX. Fix: use long.

Time: O(n). Space: O(1).

Problem 2 — Best Time to Buy and Sell Stock (LC 121)

Difficulty: Easy. Pattern: implicit fixed-size (window of 2).

Why it counts as sliding window: you're tracking a window of two prices (min-so-far, current). The "window" is conceptual.

def maxProfit(prices):
    min_price = float('inf')
    best = 0
    for price in prices:
        min_price = min(min_price, price)
        best = max(best, price - min_price)
    return best

Time: O(n). Space: O(1).

Problem 3 — Longest Substring Without Repeating Characters (LC 3)

Difficulty: Medium. Pattern: variable-size.

Walkthrough: maintain a set of chars in the current window. Expand right. Shrink left when duplicate.

def lengthOfLongestSubstring(s):
    seen = set()
    left = 0
    best = 0
    for right in range(len(s)):
        while s[right] in seen:
            seen.remove(s[left])
            left += 1
        seen.add(s[right])
        best = max(best, right - left + 1)
    return best

Common mistake: removing the wrong char when shrinking (some candidates write seen.remove(s[right])). Fix: always shrink one char at a time from the left.

Time: O(n). Space: O(min(n, alphabet_size)).

Problem 4 — Minimum Window Substring (LC 76)

Difficulty: Hard. Pattern: variable-size.

The flagship sliding window problem. Asked at every FAANG, tagged as the canonical "did you actually understand sliding window" filter.

Walkthrough: maintain need (target char counts) and have (count of chars in current window). Expand right. Once have == need, shrink left while still valid. Record the minimum.

from collections import Counter

def minWindow(s, t):
    if not t or not s:
        return ""
    need = Counter(t)
    have = {}
    left = 0
    formed = 0
    required = len(need)
    best = (float('inf'), 0, 0)  # (length, left, right)
    for right, char in enumerate(s):
        have[char] = have.get(char, 0) + 1
        if char in need and have[char] == need[char]:
            formed += 1
        while formed == required:
            if right - left + 1 < best[0]:
                best = (right - left + 1, left, right)
            have[s[left]] -= 1
            if s[left] in need and have[s[left]] < need[s[left]]:
                formed -= 1
            left += 1
    return "" if best[0] == float('inf') else s[best[1]:best[2]+1]

Common mistakes:

  1. Not tracking formed (the count of chars whose required-count is met). Trying to compare Counter objects every iteration is O(alphabet) per step.
  2. Decrementing formed BEFORE incrementing left. Fix: decrement only when have[s[left]] strictly drops below need[s[left]].
  3. Forgetting the empty-string return case.

Time: O(n + m). Space: O(alphabet).

For the deep dive on this problem alone, see LeetCode 76 Minimum Window Substring walkthrough.

Problem 5 — Permutation in String (LC 567)

Difficulty: Medium. Pattern: fixed-size + char-count match.

Walkthrough: window of length(s1). Compare frequency arrays. Slide one char at a time, updating in O(1).

def checkInclusion(s1, s2):
    if len(s1) > len(s2):
        return False
    s1_count = [0] * 26
    s2_count = [0] * 26
    for i in range(len(s1)):
        s1_count[ord(s1[i]) - ord('a')] += 1
        s2_count[ord(s2[i]) - ord('a')] += 1
    if s1_count == s2_count:
        return True
    for i in range(len(s1), len(s2)):
        s2_count[ord(s2[i]) - ord('a')] += 1
        s2_count[ord(s2[i - len(s1)]) - ord('a')] -= 1
        if s1_count == s2_count:
            return True
    return False

Time: O(n * 26) = O(n). Space: O(1).

Problem 6 — Find All Anagrams in a String (LC 438)

Variant of LC 567. Same template. Append start index to result list instead of returning bool on first match.

def findAnagrams(s, p):
    if len(p) > len(s):
        return []
    p_count = [0] * 26
    s_count = [0] * 26
    for i in range(len(p)):
        p_count[ord(p[i]) - ord('a')] += 1
        s_count[ord(s[i]) - ord('a')] += 1
    result = []
    if p_count == s_count:
        result.append(0)
    for i in range(len(p), len(s)):
        s_count[ord(s[i]) - ord('a')] += 1
        s_count[ord(s[i - len(p)]) - ord('a')] -= 1
        if p_count == s_count:
            result.append(i - len(p) + 1)
    return result

Problem 7 — Longest Repeating Character Replacement (LC 424)

Difficulty: Medium. Pattern: variable-size with k-flip constraint.

The "max-freq trick": window is valid if (window_size - max_freq) <= k. Invalid? Shrink left.

def characterReplacement(s, k):
    freq = {}
    left = 0
    max_freq = 0
    best = 0
    for right in range(len(s)):
        freq[s[right]] = freq.get(s[right], 0) + 1
        max_freq = max(max_freq, freq[s[right]])
        while (right - left + 1) - max_freq > k:
            freq[s[left]] -= 1
            left += 1
        best = max(best, right - left + 1)
    return best

Subtle point: max_freq doesn't need to be re-computed when shrinking. The window size only matters relative to the max_freq seen at expansion time. One of the three "trick problems" in the sliding window canon.

Time: O(n). Space: O(alphabet).

Problem 8 — Sliding Window Maximum (LC 239)

Difficulty: Hard. Pattern: monotonic deque (NOT vanilla sliding window).

Why this problem trips candidates: it LOOKS like sliding window (the words "sliding window" are literally in the title), but you can't track the maximum with a simple variable. You need a monotonic decreasing deque.

from collections import deque

def maxSlidingWindow(nums, k):
    dq = deque()  # stores indices
    result = []
    for i in range(len(nums)):
        while dq and dq[0] < i - k + 1:
            dq.popleft()
        while dq and nums[dq[-1]] < nums[i]:
            dq.pop()
        dq.append(i)
        if i >= k - 1:
            result.append(nums[dq[0]])
    return result

The deque holds indices of candidates for "max in current window." Front of deque is current max. Pop from the back when a new larger element arrives (those candidates can never win again).

For the deep dive on monotonic deque + monotonic stack patterns, see Monotonic Stack Pattern Guide.

Time: O(n). Space: O(k).

Problem 9 — Fruit Into Baskets (LC 904)

Two-distinct-element variable-size window. The "at most 2 distinct" specialization of LC 340.

def totalFruit(fruits):
    freq = {}
    left = 0
    best = 0
    for right in range(len(fruits)):
        freq[fruits[right]] = freq.get(fruits[right], 0) + 1
        while len(freq) > 2:
            freq[fruits[left]] -= 1
            if freq[fruits[left]] == 0:
                del freq[fruits[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

Problem 10 — Subarrays with K Different Integers (LC 992)

The "exactly K equals at-most K minus at-most K-1" trick. One of the three pattern-recognition unlocks in sliding window.

def subarraysWithKDistinct(nums, k):
    def at_most(K):
        freq = {}
        left = 0
        count = 0
        for right in range(len(nums)):
            freq[nums[right]] = freq.get(nums[right], 0) + 1
            while len(freq) > K:
                freq[nums[left]] -= 1
                if freq[nums[left]] == 0:
                    del freq[nums[left]]
                left += 1
            count += right - left + 1
        return count
    return at_most(k) - at_most(k - 1)

The count += right - left + 1 line counts all subarrays ending at right that satisfy "at most K distinct." The difference gives "exactly K."

Problem 11 — Longest Substring with At Most K Distinct (LC 340)

Variable-size with frequency map. The base case for LC 992's at_most helper.

def lengthOfLongestSubstringKDistinct(s, k):
    freq = {}
    left = 0
    best = 0
    for right in range(len(s)):
        freq[s[right]] = freq.get(s[right], 0) + 1
        while len(freq) > k:
            freq[s[left]] -= 1
            if freq[s[left]] == 0:
                del freq[s[left]]
            left += 1
        best = max(best, right - left + 1)
    return best

Problem 12 — Minimum Size Subarray Sum (LC 209)

Variable-size minimum window with sum constraint.

def minSubArrayLen(target, nums):
    left = 0
    total = 0
    best = float('inf')
    for right in range(len(nums)):
        total += nums[right]
        while total >= target:
            best = min(best, right - left + 1)
            total -= nums[left]
            left += 1
    return 0 if best == float('inf') else best

Note the inverted shrink condition: shrink while still valid (find minimum). Compare to LC 76, the same pattern with multi-character constraint.

Problem 13 — Max Consecutive Ones III (LC 1004)

Variable-size with k-flips. "What's the longest subarray containing at most k zeros?"

def longestOnes(nums, k):
    left = 0
    zeros = 0
    best = 0
    for right in range(len(nums)):
        if nums[right] == 0:
            zeros += 1
        while zeros > k:
            if nums[left] == 0:
                zeros -= 1
            left += 1
        best = max(best, right - left + 1)
    return best

Problem 14 — Substring with Concatenation of All Words (LC 30)

Hard variant. Multi-word fixed-size window. The trick is that you slide by word-length not by 1.

from collections import Counter

def findSubstring(s, words):
    if not words:
        return []
    word_len = len(words[0])
    word_count = len(words)
    total_len = word_len * word_count
    target = Counter(words)
    result = []
    for i in range(word_len):
        left = i
        seen = Counter()
        count = 0
        right = i
        while right + word_len <= len(s):
            word = s[right:right + word_len]
            right += word_len
            if word in target:
                seen[word] += 1
                count += 1
                while seen[word] > target[word]:
                    seen[s[left:left + word_len]] -= 1
                    left += word_len
                    count -= 1
                if count == word_count:
                    result.append(left)
            else:
                seen.clear()
                count = 0
                left = right
    return result

Problem 15 — Get Equal Substrings Within Budget (LC 1208)

Variable-size with cost budget constraint.

def equalSubstring(s, t, maxCost):
    left = 0
    cost = 0
    best = 0
    for right in range(len(s)):
        cost += abs(ord(s[right]) - ord(t[right]))
        while cost > maxCost:
            cost -= abs(ord(s[left]) - ord(t[left]))
            left += 1
        best = max(best, right - left + 1)
    return best

Problem 16 — Number of Subarrays with Bounded Maximum (LC 795)

Counting sliding window variant.

def numSubarrayBoundedMax(nums, left, right):
    def count_at_most(bound):
        total = 0
        run = 0
        for n in nums:
            run = run + 1 if n <= bound else 0
            total += run
        return total
    return count_at_most(right) - count_at_most(left - 1)

Same trick as LC 992: at_most(R) - at_most(L-1) = exactly-in-range.

Problem 17 — Maximum Number of Vowels in a Substring (LC 1456)

Easy fixed-size warmup.

def maxVowels(s, k):
    vowels = set('aeiou')
    count = sum(1 for c in s[:k] if c in vowels)
    best = count
    for i in range(k, len(s)):
        count += (1 if s[i] in vowels else 0) - (1 if s[i - k] in vowels else 0)
        best = max(best, count)
    return best

Problem 18 — Longest Subarray of 1s After Deleting One Element (LC 1493)

Variable-size with "delete-one" constraint. This is essentially LC 1004 with k=1 but framed differently.

def longestSubarray(nums):
    left = 0
    zeros = 0
    best = 0
    for right in range(len(nums)):
        if nums[right] == 0:
            zeros += 1
        while zeros > 1:
            if nums[left] == 0:
                zeros -= 1
            left += 1
        best = max(best, right - left)  # right - left, NOT +1, because we delete one
    return best

Sliding window vs two pointers — the decision tree

Two pointers and sliding window look similar (both use two indices). The distinction:

  • Two pointers: usually opposite-end (sorted array, palindrome check) OR same-direction with no shrinking. No window-state aggregate.
  • Sliding window: same-direction WITH shrinking plus window aggregate (sum, frequency map, count).

Cheat: maintain a "window state" (frequency map, sum, count)? Sliding window. Only compare two positions (e.g., arr[left] + arr[right] == target)? Two pointers.

For the two-pointers companion guide, see Two Pointers Pattern Guide.

Sliding window vs prefix sum — the decision tree

Prefix sum is the alternative when sliding window fails. The failure case for sliding window is negative numbers in a sum-constrained problem.

  • Prefix sum: when the question is "how many subarrays sum to K" or "subarray sum equals K", especially with negative numbers in the array. Sliding window doesn't work because expanding right with a negative number can increase or decrease the sum unpredictably.
  • Sliding window: when the question has a monotonic constraint (sum ≥ K, count ≤ K, max-freq) AND the array is non-negative.

Common sliding window mistakes

After watching candidates work through these problems, the same five mistakes show up.

Mistake 1: Recording answer BEFORE shrinking left. Symptom: your "longest" answer is too long because you record before the constraint is re-validated. Fix: shrink-then-record discipline. The best = max(...) line goes AFTER the inner while loop.

Mistake 2: Updating window aggregate AFTER moving the pointer. Symptom: off-by-one errors in fixed-size template. Fix: aggregate-update happens with the pointer move, in the same statement when possible.

Mistake 3: Using sliding window on negative-number arrays for "sum ≥ K" problems. Symptom: TLE or wrong answer. Fix: use prefix sum + monotonic deque (LC 862).

Mistake 4: Forgetting to handle the empty-window edge case. Symptom: index-out-of-bounds on input s = "" or nums = []. Fix: explicit empty-check at the top of the function.

Mistake 5: Sliding window with hashmap instead of array of 26 for lowercase-only-string problems. Symptom: 10x slower on LC tests. Fix: when the alphabet is bounded (26 lowercase, 128 ASCII), use a fixed-size array. Hashmaps carry constant-factor overhead that's ~10x slower in CPython.

Sliding window in 2026 — interviewer expectations

The 2026 interviewer expectation is that you can:

  • Recognize sliding window in under 2 minutes from problem statement.
  • Code the variable-size template in under 8 minutes for medium problems.
  • Answer edge-case probes confidently: "what if the array has negative numbers?" "what if K can be 0?" "what if the string is empty?"
  • Distinguish from adjacent patterns (two pointers, prefix sum, monotonic deque) when prompted.

The bar is higher than 2022 because AI tools commoditized the basic templates. Interviewers now probe for the next level of understanding — the variant that's not in your study list.

Practice plan — the 18 problems in order

Two-week practice plan.

  • Tier 1 (do first): LC 121, 643, 3, 209.
  • Tier 2 (medium grind): LC 76, 567, 438, 424, 1004, 340.
  • Tier 3 (hard finishers): LC 239, 992, 30, 1493.

Aim for Tier 1 in 15 minutes each by end of week 1, Tier 2 in 25 minutes each by end of week 2, Tier 3 in 40 minutes each by end of week 2.

Track your time per problem. Consistently over the budget? The problem isn't your speed. You haven't internalized the template yet. Re-read the template, re-do the problem cold the next day.

For the parent pillar covering all 23 patterns FAANG draws from, see 23 LeetCode Patterns That Cover 95% of FAANG Questions. For the frequency-ranked problem list (sliding window hits 22% of FAANG screens), see 75 LeetCode Problems FAANG Asks Most.

FAQ

Is sliding window the same as two pointers? No. See decision tree above. Sliding window has window state. Two pointers usually doesn't.

Can I use sliding window on a sorted array? Sometimes. Two pointers is usually simpler. The main exception: counting subarrays with property X (sliding window's count += right - left + 1 trick).

Why does my sliding window TLE on LC 992? You're probably doing exactly-K instead of at-most-K minus at-most-K-1. Re-read Problem 10.

Which company asks sliding window the most? Meta and Google, per the Sean Prashad list and our internal Discord-tracked dataset. Amazon asks slightly less. Apple rarely.

Should I learn the fixed-size template first or variable-size? Variable-size. It's harder but covers 70% of sliding window problems. Once variable-size is fluent, fixed-size is trivial.

The verdict

Sliding window is one of the three highest-value patterns to master alongside two pointers and BFS/DFS. Two templates. Eighteen problems. Two weeks of focused practice.

If you found this useful, FaangCoder helps candidates iterate to optimal solutions in real interviews. Try it on LC 76 to see how the AI walks through the formed counter trick step by step. $399 lifetime ($199/mo monthly option). See the Solve demo, see the Debug demo, or join the Discord.

FaangCoder

Iterate to the optimal solution. In three keystrokes.

FaangCoder reads your problem, code, and terminal directly from memory. No screenshots, no waiting. Solve, Debug, and Optimize iteratively until the answer is right.