LeetCode Patterns - 02 Sliding Window

Post by ailswan June. 02, 2026

中文 ↓

🎯 Sliding Window

1️⃣ Core Framework

When discussing Sliding Window, I frame it as:

  1. What problem pattern it solves
  2. Fixed-size window vs variable-size window
  3. Window state
  4. Window validity condition
  5. Expand and shrink logic
  6. Common interview problems
  7. Edge cases
  8. Complexity analysis
  9. Senior-level explanation

2️⃣ What Problem Are We Solving?

Sliding window is used for problems involving contiguous subarrays or substrings.

It helps answer questions like:

The key idea is:

Instead of recomputing every subarray from scratch,
maintain a moving window and update its state incrementally.

👉 Interview Answer

Sliding window is a two-pointer technique used for contiguous subarray or substring problems.

The right pointer expands the window, and the left pointer shrinks the window when the window becomes invalid.

By maintaining window state incrementally, we avoid checking all subarrays with nested loops and often reduce the time complexity from O(n²) to O(n).


3️⃣ Why Sliding Window Works


Brute Force Problem

A brute-force solution often checks all subarrays:

for left in range(n):
    for right in range(left, n):
        check nums[left:right]

This is usually:

O(n²)

or worse if checking the subarray also takes time.


Sliding Window Optimization

Sliding window avoids recomputing from scratch.

left → window → right

We maintain:

current sum
current count
current frequency map
current distinct count
current max/min condition

When right moves, we add a new element.

When left moves, we remove an old element.


Core Rule

Expand right to include more elements.
Shrink left when the window violates the condition.
Update the answer when the window is valid.

👉 Interview Answer

Sliding window works because the window changes gradually.

When the right pointer moves, we add one element.

When the left pointer moves, we remove one element.

Since each element enters and leaves the window at most once, the total time is usually O(n).


4️⃣ Sliding Window Types


Type 1: Fixed-Size Window

The window size is fixed.

Examples:

Maximum sum of subarray of size k
Average of subarrays of size k
Find all anagrams in a string

Pattern:

Expand right.
When window size reaches k, update answer.
Then remove left and slide forward.

Type 2: Variable-Size Window

The window size changes based on a condition.

Examples:

Longest substring without repeating characters
Minimum size subarray sum
Minimum window substring
Longest substring with at most K distinct characters

Pattern:

Expand right.
While window is invalid, shrink left.
Update answer when valid.

👉 Interview Answer

I divide sliding window problems into fixed-size and variable-size windows.

Fixed-size windows maintain exactly k elements.

Variable-size windows grow and shrink based on a validity condition.

The key is knowing when to expand, when to shrink, and when to update the result.


5️⃣ Fixed-Size Sliding Window


Problem

Find the maximum sum of a subarray of size k.

nums = [1, 2, 3, 4, 5], k = 3
answer = 12
because [3, 4, 5] has the largest sum

Code

def max_sum_subarray(nums, k):
    window_sum = 0
    best = float("-inf")

    for right in range(len(nums)):
        window_sum += nums[right]

        if right >= k - 1:
            best = max(best, window_sum)
            window_sum -= nums[right - k + 1]

    return best

Explanation

When the window reaches size k:

1. Update answer.
2. Remove the leftmost element.
3. Continue sliding.

👉 Interview Answer

For fixed-size sliding window problems, I maintain a window of size k.

Each time I add the right element.

Once the window reaches size k, I update the answer and remove the leftmost element.

This gives O(n) time because every element is added once and removed once.


6️⃣ Fixed-Size Window Template


def fixed_window(nums, k):
    window_state = 0
    answer = None

    for right in range(len(nums)):
        # add nums[right] to window
        window_state += nums[right]

        if right >= k - 1:
            # update answer using current window
            answer = update(answer, window_state)

            # remove leftmost element
            left = right - k + 1
            window_state -= nums[left]

    return answer

When to Use

Use this when the problem says:

subarray of size k
substring of length k
exactly k elements
fixed-length window

👉 Interview Answer

Fixed-size sliding window is appropriate when the problem explicitly requires a window of length k.

The window size does not depend on a dynamic condition.

We simply add the new element and remove the old element as the window slides.


7️⃣ Variable-Size Sliding Window


Core Idea

The window size changes based on validity.

left = 0

for right in range(len(nums)):
    add(nums[right])

    while window_is_invalid():
        remove(nums[left])
        left += 1

    update_answer()

Key Components

Every variable-size sliding window needs:

  1. A left pointer
  2. A right pointer
  3. Window state
  4. Validity condition
  5. Answer update rule

👉 Interview Answer

Variable-size sliding window is used when we need the longest, shortest, or count of windows satisfying a condition.

The right pointer expands the window.

If the window becomes invalid, the left pointer shrinks it until it becomes valid again.

The answer is updated based on the valid window.


8️⃣ Longest Substring Without Repeating Characters


Problem

Find the length of the longest substring without repeating characters.

s = "abcabcbb"
answer = 3
because "abc" is the longest substring without duplicates

Code

def length_of_longest_substring(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

Invariant

The window always satisfies:

No duplicate characters inside the window.

👉 Interview Answer

I maintain a window with no duplicate characters.

The right pointer expands the window.

If the new character already exists in the window, I move the left pointer and remove characters until the duplicate is gone.

Then I update the maximum window length.


9️⃣ Minimum Size Subarray Sum


Problem

Given positive integers, find the minimum length subarray whose sum is at least target.

nums = [2, 3, 1, 2, 4, 3], target = 7
answer = 2
because [4, 3] has sum 7

Code

def min_subarray_len(target, nums):
    left = 0
    window_sum = 0
    best = float("inf")

    for right in range(len(nums)):
        window_sum += nums[right]

        while window_sum >= target:
            best = min(best, right - left + 1)
            window_sum -= nums[left]
            left += 1

    return 0 if best == float("inf") else best

Important Assumption

This works because numbers are positive.

If negative numbers exist, the sum is no longer monotonic.


👉 Interview Answer

For minimum size subarray sum, I expand the window until the sum reaches the target.

Once the sum is large enough, I shrink from the left to find the smallest valid window.

This works because all numbers are positive, so expanding increases the sum and shrinking decreases the sum.


🔟 At Most K Distinct Characters


Problem

Find the longest substring with at most k distinct characters.


Code

def length_of_longest_substring_k_distinct(s, k):
    from collections import defaultdict

    count = defaultdict(int)
    left = 0
    best = 0

    for right in range(len(s)):
        count[s[right]] += 1

        while len(count) > k:
            count[s[left]] -= 1

            if count[s[left]] == 0:
                del count[s[left]]

            left += 1

        best = max(best, right - left + 1)

    return best

Window State

count map: character frequency inside window
len(count): number of distinct characters

👉 Interview Answer

I use a frequency map to track characters in the current window.

The window is valid when the number of distinct characters is at most k.

If distinct count exceeds k, I shrink from the left until the window becomes valid again.

Then I update the maximum length.


1️⃣1️⃣ Minimum Window Substring


Problem

Find the smallest substring of s that contains all characters from t.

s = "ADOBECODEBANC"
t = "ABC"
answer = "BANC"

Code

def min_window(s, t):
    from collections import Counter, defaultdict

    need = Counter(t)
    window = defaultdict(int)

    have = 0
    need_count = len(need)

    left = 0
    best_len = float("inf")
    best_start = 0

    for right in range(len(s)):
        char = s[right]
        window[char] += 1

        if char in need and window[char] == need[char]:
            have += 1

        while have == need_count:
            if right - left + 1 < best_len:
                best_len = right - left + 1
                best_start = left

            left_char = s[left]
            window[left_char] -= 1

            if left_char in need and window[left_char] < need[left_char]:
                have -= 1

            left += 1

    if best_len == float("inf"):
        return ""

    return s[best_start:best_start + best_len]

Key State

need: required character counts
window: current window counts
have: number of characters whose required count is satisfied
need_count: total unique required characters

👉 Interview Answer

Minimum window substring is a variable-size sliding window problem.

I expand the right pointer until the window contains all required characters.

Once valid, I shrink from the left to find the smallest valid window.

I use frequency maps to track required counts and current window counts.


1️⃣2️⃣ Find All Anagrams in a String


Problem

Find all start indexes of p’s anagrams in s.

s = "cbaebabacd"
p = "abc"
answer = [0, 6]

Fixed-Size Window

Because every anagram of p has length:

len(p)

Code

def find_anagrams(s, p):
    from collections import Counter, defaultdict

    need = Counter(p)
    window = defaultdict(int)
    result = []

    k = len(p)

    for right in range(len(s)):
        window[s[right]] += 1

        if right >= k:
            left_char = s[right - k]
            window[left_char] -= 1

            if window[left_char] == 0:
                del window[left_char]

        if window == need:
            result.append(right - k + 1)

    return result

👉 Interview Answer

Finding anagrams is a fixed-size sliding window problem.

Every anagram has the same length as p.

I maintain a frequency map for the current window and compare it with the target frequency map.

When they match, the current window is an anagram.


1️⃣3️⃣ Counting Valid Subarrays


Common Pattern

Sometimes the problem asks:

How many subarrays satisfy a condition?

For example:

number of subarrays with at most k distinct integers

Key Trick

If a window [left, right] is valid, then every subarray ending at right and starting between left and right is also valid.

So we add:

right - left + 1

Code Pattern

def count_at_most_k(nums, k):
    from collections import defaultdict

    count = defaultdict(int)
    left = 0
    result = 0

    for right in range(len(nums)):
        count[nums[right]] += 1

        while len(count) > k:
            count[nums[left]] -= 1

            if count[nums[left]] == 0:
                del count[nums[left]]

            left += 1

        result += right - left + 1

    return result

👉 Interview Answer

For counting valid subarrays, once the window is valid, all subarrays ending at right and starting from any index between left and right are also valid.

Therefore, we can add right - left + 1 to the answer.

This is a common trick for at-most-k style sliding window problems.


1️⃣4️⃣ Exactly K Problems


Important Technique

Problems asking for exactly k are often solved using at most k.

exactly(k) = at_most(k) - at_most(k - 1)

Example

Number of subarrays with exactly k distinct integers:

def subarrays_with_k_distinct(nums, k):
    return at_most_k(nums, k) - at_most_k(nums, k - 1)

Why It Works

Subarrays with at most k
minus
Subarrays with at most k - 1
equals
Subarrays with exactly k

👉 Interview Answer

For exactly-k sliding window problems, I often transform the problem into two at-most-k problems.

The formula is exactly(k) equals at_most(k) minus at_most(k - 1).

This is useful because at-most-k windows are usually easier to maintain.


1️⃣5️⃣ Window State Design


Common Window States

Problem Type Window State
Sum problem current sum
Product problem current product
Duplicate problem set or frequency map
Distinct count problem frequency map
Min/max problem monotonic deque
Required character problem need/window count maps

Senior-Level Question

Ask:

What do I need to know about the current window
to decide whether it is valid?

👉 Interview Answer

The most important design decision is the window state.

For sum problems, state may be a running sum.

For duplicate or distinct problems, state is usually a set or frequency map.

For minimum or maximum inside a window, I may need a monotonic deque.

The state should allow me to check validity efficiently.


1️⃣6️⃣ When Sliding Window Does Not Work


Negative Numbers Can Break Monotonicity

For example:

Minimum subarray sum >= target

Sliding window works when all numbers are positive.

But if numbers can be negative:

[2, -1, 2]

Expanding does not always increase sum.

Shrinking does not always decrease sum.


Non-Contiguous Problems

Sliding window is for contiguous ranges.

If the problem asks for subsequence or arbitrary subset, sliding window may not apply.


No Efficient Validity Update

If the window condition cannot be updated incrementally, sliding window may not help.


👉 Interview Answer

Sliding window is not always valid.

It usually requires a contiguous range and some form of monotonic or incrementally maintainable condition.

If negative numbers break monotonicity, or if the problem is about subsequences rather than substrings or subarrays, sliding window may not be the right approach.


1️⃣7️⃣ Sliding Window vs Two Pointers


Relationship

Sliding window is a specialized two-pointer pattern.

Two pointers: general technique
Sliding window: two pointers maintaining a contiguous range

Two Pointers

Can be:


Sliding Window

Always represents:

a contiguous window [left, right]

👉 Interview Answer

Sliding window is a specific type of two pointers.

Two pointers is the broader category.

Sliding window specifically maintains a contiguous range between left and right.

It is mostly used for substring and subarray problems.


1️⃣8️⃣ Common Edge Cases


Empty Input

s = ""
nums = []

k Equals 0

k = 0

Usually requires special handling.


k Greater Than Length

k > len(nums)

For fixed-size windows, no valid window may exist.


Duplicate Characters

Important for substring problems.


Need to Update Before or After Shrinking

Some problems require:

update answer before shrinking

Others require:

update answer after shrinking

This depends on whether we want minimum or maximum window.


👉 Interview Answer

The main edge cases are empty input, k equals zero, k larger than the input length, duplicates, and update timing.

I always clarify whether the answer should be updated when the window first becomes valid or after it is shrunk to its smallest valid form.


1️⃣9️⃣ Complexity Analysis


Time Complexity

Most sliding window algorithms are:

O(n)

Because:

right moves from 0 to n - 1
left also moves from 0 to n - 1

Each element is added once and removed once.


Space Complexity

Depends on window state:

O(1) for running sum
O(k) or O(charset size) for frequency map
O(n) worst case for hash map

👉 Interview Answer

Sliding window is usually O(n) because each pointer moves monotonically.

Each element enters the window once and leaves the window once.

Space complexity depends on the state we maintain, such as a running sum, set, frequency map, or deque.


2️⃣0️⃣ Common Mistakes


Mistake 1: Using Sliding Window When Condition Is Not Monotonic

Example:

negative numbers in minimum subarray sum

Mistake 2: Updating Answer at the Wrong Time

For longest valid window:

update after making window valid

For minimum valid window:

update while window is valid before shrinking

Mistake 3: Forgetting to Remove from Frequency Map

if count[x] == 0:
    del count[x]

Mistake 4: Infinite Loop

Forgetting to move left inside the while loop.


Mistake 5: Confusing Fixed and Variable Window

Fixed-size:

window size is exactly k

Variable-size:

window size depends on condition

👉 Interview Answer

The most common mistakes are applying sliding window when the condition is not monotonic, updating the answer at the wrong time, forgetting to remove zero-count keys from the map, and confusing fixed-size with variable-size windows.

I avoid these mistakes by first defining the window invariant.


2️⃣1️⃣ Standard Templates


Fixed-Size Window Template

def fixed_window(nums, k):
    window_state = 0
    answer = None

    for right in range(len(nums)):
        add nums[right] to window_state

        if right >= k - 1:
            update answer

            left = right - k + 1
            remove nums[left] from window_state

    return answer

Variable-Size Longest Window Template

def longest_valid_window(nums):
    left = 0
    answer = 0

    for right in range(len(nums)):
        add(nums[right])

        while window_invalid():
            remove(nums[left])
            left += 1

        answer = max(answer, right - left + 1)

    return answer

Variable-Size Minimum Window Template

def minimum_valid_window(nums):
    left = 0
    answer = float("inf")

    for right in range(len(nums)):
        add(nums[right])

        while window_valid():
            answer = min(answer, right - left + 1)
            remove(nums[left])
            left += 1

    return answer

Count Valid Subarrays Template

def count_valid_windows(nums):
    left = 0
    answer = 0

    for right in range(len(nums)):
        add(nums[right])

        while window_invalid():
            remove(nums[left])
            left += 1

        answer += right - left + 1

    return answer

🧠 Staff-Level Answer Final


👉 Interview Answer Full Version

Sliding window is a two-pointer technique used for contiguous subarray or substring problems.

The core idea is to maintain a window between left and right pointers and update the window state incrementally as the window moves.

Instead of recomputing every possible subarray or substring from scratch, we add one element when the right pointer expands and remove one element when the left pointer shrinks.

This often reduces brute-force O(n²) solutions to O(n).

I usually separate sliding window problems into fixed-size and variable-size windows.

Fixed-size windows are used when the problem asks for exactly k elements or a substring of length k. In that case, we add the new right element, update the answer when the window reaches size k, and then remove the leftmost element.

Variable-size windows are used when the window size depends on a condition. The right pointer expands the window. If the window becomes invalid, the left pointer shrinks the window until it becomes valid again.

The most important part of sliding window is defining the window state and the invariant.

For sum problems, the state may be a running sum. For duplicate or distinct-character problems, the state is usually a set or frequency map. For minimum or maximum values inside a window, we may need a monotonic deque.

I also check whether sliding window is actually valid for the problem. It usually requires a contiguous range and an incrementally maintainable condition. Some problems with negative numbers break the monotonic behavior required by sliding window.

Complexity is usually O(n), because each element enters the window once and leaves the window once. Space complexity depends on the window state, such as O(1) for a running sum or O(k) / O(n) for a frequency map.

At a senior level, I would not just say “use sliding window.” I would explain what the window represents, what makes the window valid or invalid, how the state is updated, why each pointer moves monotonically, and why no candidate answer is skipped.


⭐ Final Insight

Sliding Window 的核心不是简单地移动 left 和 right。

它的核心是维护一个 window invariant。

Senior-level 的解释重点是:

当前 window 代表什么, window state 如何维护, window 什么时候 valid, 什么时候 shrink, 以及为什么每个元素最多进出窗口一次。

能讲清楚这些,Sliding Window 就从刷题模板变成了真正的 algorithmic reasoning pattern。



中文部分


🎯 Sliding Window


1️⃣ 核心框架

讨论 Sliding Window 时,我通常从:

  1. 它解决什么问题
  2. 固定窗口 vs 可变窗口
  3. Window state
  4. Window validity condition
  5. Expand 和 shrink 逻辑
  6. 高频面试题
  7. Edge cases
  8. Complexity analysis
  9. 高级工程师解释方式

2️⃣ 要解决什么问题?

Sliding window 主要用于 连续 subarraysubstring 问题。

它常用来解决:

核心思想是:

不要每次重新计算一个 subarray 或 substring,
而是维护一个移动窗口,
并且增量更新窗口状态。

👉 面试回答

Sliding window 是一种 two-pointer 技巧, 主要用于连续 subarray 或 substring 问题。

right pointer 负责扩展窗口, left pointer 在窗口不合法时负责收缩窗口。

通过增量维护 window state, 我们可以避免 O(n²) 的暴力枚举, 通常把复杂度降到 O(n)。


3️⃣ 为什么 Sliding Window 有效?


暴力解法

很多问题的暴力解是检查所有 subarray:

for left in range(n):
    for right in range(left, n):
        check nums[left:right]

复杂度通常是:

O(n²)

如果每次还要重新计算窗口状态,可能更慢。


Sliding Window 优化

Sliding window 不重新计算整个区间。

left → window → right

我们维护:

current sum
current count
current frequency map
current distinct count
current max/min condition

right 移动时,加入新元素。

left 移动时,移除旧元素。


核心规则

Expand right to include more elements.
Shrink left when the window violates the condition.
Update the answer when the window is valid.

👉 面试回答

Sliding window 有效,是因为窗口每次只变化一个元素。

right pointer 移动时,加入一个元素。

left pointer 移动时,移除一个元素。

每个元素最多进入窗口一次、离开窗口一次, 所以总复杂度通常是 O(n)。


4️⃣ Sliding Window 类型


Type 1: 固定大小窗口

窗口大小固定。

例如:

Maximum sum of subarray of size k
Average of subarrays of size k
Find all anagrams in a string

模式:

right 扩展窗口。
当窗口大小达到 k,更新答案。
然后移除 left,窗口向前滑动。

Type 2: 可变大小窗口

窗口大小根据条件变化。

例如:

Longest substring without repeating characters
Minimum size subarray sum
Minimum window substring
Longest substring with at most K distinct characters

模式:

right 扩展窗口。
当 window invalid 时,移动 left 收缩窗口。
当 window valid 时,更新答案。

👉 面试回答

我会把 sliding window 分成 fixed-size 和 variable-size 两类。

Fixed-size window 用于窗口长度明确是 k 的问题。

Variable-size window 用于窗口大小由条件决定的问题。

关键是判断什么时候 expand,什么时候 shrink,以及什么时候 update answer。


5️⃣ 固定大小 Sliding Window


Problem

找 size 为 k 的 subarray 的最大 sum。

nums = [1, 2, 3, 4, 5], k = 3
answer = 12
because [3, 4, 5] has the largest sum

Code

def max_sum_subarray(nums, k):
    window_sum = 0
    best = float("-inf")

    for right in range(len(nums)):
        window_sum += nums[right]

        if right >= k - 1:
            best = max(best, window_sum)
            window_sum -= nums[right - k + 1]

    return best

Explanation

当窗口达到 size k:

1. 更新答案
2. 移除最左边元素
3. 继续滑动

👉 面试回答

对固定大小窗口问题, 我维护一个 size 为 k 的 window。

每次加入 right 指向的新元素。

当窗口达到 k 个元素时, 更新答案,然后移除最左边元素。

因为每个元素只加入一次、移除一次, 所以复杂度是 O(n)。


6️⃣ 固定窗口模板


def fixed_window(nums, k):
    window_state = 0
    answer = None

    for right in range(len(nums)):
        # add nums[right] to window
        window_state += nums[right]

        if right >= k - 1:
            # update answer using current window
            answer = update(answer, window_state)

            # remove leftmost element
            left = right - k + 1
            window_state -= nums[left]

    return answer

什么时候用?

当题目说:

subarray of size k
substring of length k
exactly k elements
fixed-length window

👉 面试回答

Fixed-size sliding window 适合窗口长度明确固定的问题。

window size 不依赖动态条件。

我们只需要每次加入新元素, 当窗口达到 k 时更新答案, 然后移除最左边元素。


7️⃣ 可变大小 Sliding Window


核心思想

窗口大小根据条件变化。

left = 0

for right in range(len(nums)):
    add(nums[right])

    while window_is_invalid():
        remove(nums[left])
        left += 1

    update_answer()

关键组成

每个 variable-size sliding window 都需要:

  1. left pointer
  2. right pointer
  3. window state
  4. validity condition
  5. answer update rule

👉 面试回答

Variable-size sliding window 用于寻找满足条件的最长、最短,或者统计满足条件的窗口数量。

right pointer 负责扩展窗口。

如果窗口变得 invalid, left pointer 负责收缩窗口,直到窗口重新 valid。

答案根据 valid window 更新。


8️⃣ Longest Substring Without Repeating Characters


Problem

找最长的不含重复字符的 substring。

s = "abcabcbb"
answer = 3
because "abc" is the longest substring without duplicates

Code

def length_of_longest_substring(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

Invariant

窗口始终满足:

window 中没有重复字符

👉 面试回答

我维护一个没有重复字符的窗口。

right pointer 扩展窗口。

如果新加入的字符已经存在, 就移动 left pointer 并移除字符, 直到 duplicate 被移除。

然后更新最大窗口长度。


9️⃣ Minimum Size Subarray Sum


Problem

给定正整数数组,找到 sum 至少为 target 的最短 subarray 长度。

nums = [2, 3, 1, 2, 4, 3], target = 7
answer = 2
because [4, 3] has sum 7

Code

def min_subarray_len(target, nums):
    left = 0
    window_sum = 0
    best = float("inf")

    for right in range(len(nums)):
        window_sum += nums[right]

        while window_sum >= target:
            best = min(best, right - left + 1)
            window_sum -= nums[left]
            left += 1

    return 0 if best == float("inf") else best

重要前提

这个解法成立是因为数字都是 positive。

如果有 negative numbers:

sum 不再单调

👉 面试回答

对 minimum size subarray sum, 我先扩展 right pointer,直到 window sum 达到 target。

一旦 window valid, 我就移动 left pointer 尝试缩小窗口, 找到最短的 valid window。

这个方法成立的原因是所有数都是正数, 所以扩展会增加 sum,收缩会减少 sum。


🔟 At Most K Distinct Characters


Problem

找最多包含 k 个 distinct characters 的最长 substring。


Code

def length_of_longest_substring_k_distinct(s, k):
    from collections import defaultdict

    count = defaultdict(int)
    left = 0
    best = 0

    for right in range(len(s)):
        count[s[right]] += 1

        while len(count) > k:
            count[s[left]] -= 1

            if count[s[left]] == 0:
                del count[s[left]]

            left += 1

        best = max(best, right - left + 1)

    return best

Window State

count map: character frequency inside window
len(count): distinct character count

👉 面试回答

我用 frequency map 记录当前窗口中每个字符的出现次数。

当 distinct count 小于等于 k 时,窗口 valid。

如果 distinct count 超过 k, 就移动 left pointer 收缩窗口, 直到窗口重新 valid。

然后更新最大长度。


1️⃣1️⃣ Minimum Window Substring


Problem

找 s 中最短的 substring,使它包含 t 中所有字符。

s = "ADOBECODEBANC"
t = "ABC"
answer = "BANC"

Code

def min_window(s, t):
    from collections import Counter, defaultdict

    need = Counter(t)
    window = defaultdict(int)

    have = 0
    need_count = len(need)

    left = 0
    best_len = float("inf")
    best_start = 0

    for right in range(len(s)):
        char = s[right]
        window[char] += 1

        if char in need and window[char] == need[char]:
            have += 1

        while have == need_count:
            if right - left + 1 < best_len:
                best_len = right - left + 1
                best_start = left

            left_char = s[left]
            window[left_char] -= 1

            if left_char in need and window[left_char] < need[left_char]:
                have -= 1

            left += 1

    if best_len == float("inf"):
        return ""

    return s[best_start:best_start + best_len]

Key State

need: required character counts
window: current window counts
have: 已经满足要求的字符种类数
need_count: 需要满足的字符种类数

👉 面试回答

Minimum window substring 是 variable-size sliding window。

我用 right pointer 扩展窗口, 直到窗口包含所有 required characters。

当窗口 valid 后, 我移动 left pointer 尝试缩小窗口, 并记录最短 valid window。

这里需要 frequency map 来维护 required count 和 current window count。


1️⃣2️⃣ Find All Anagrams in a String


Problem

找出 s 中所有 p 的 anagram 的起始 index。

s = "cbaebabacd"
p = "abc"
answer = [0, 6]

为什么是固定窗口?

因为每个 anagram 的长度都是:

len(p)

Code

def find_anagrams(s, p):
    from collections import Counter, defaultdict

    need = Counter(p)
    window = defaultdict(int)
    result = []

    k = len(p)

    for right in range(len(s)):
        window[s[right]] += 1

        if right >= k:
            left_char = s[right - k]
            window[left_char] -= 1

            if window[left_char] == 0:
                del window[left_char]

        if window == need:
            result.append(right - k + 1)

    return result

👉 面试回答

Find all anagrams 是 fixed-size sliding window。

因为每个 anagram 的长度都等于 p 的长度。

我维护当前窗口的 frequency map, 并和 p 的 frequency map 比较。

如果相等,说明当前窗口是一个 anagram。


1️⃣3️⃣ 统计 Valid Subarrays


常见题型

有些题会问:

有多少个 subarrays 满足条件?

例如:

number of subarrays with at most k distinct integers

核心技巧

如果 [left, right] 是 valid window,

那么所有以 right 结尾、起点在 leftright 之间的 subarray 都是 valid。

所以加:

right - left + 1

Code Pattern

def count_at_most_k(nums, k):
    from collections import defaultdict

    count = defaultdict(int)
    left = 0
    result = 0

    for right in range(len(nums)):
        count[nums[right]] += 1

        while len(count) > k:
            count[nums[left]] -= 1

            if count[nums[left]] == 0:
                del count[nums[left]]

            left += 1

        result += right - left + 1

    return result

👉 面试回答

对 count valid subarrays 的题, 如果当前窗口 [left, right] 是 valid, 那么所有以 right 结尾、从 left 到 right 任意位置开始的 subarray 都是 valid。

所以每次可以加 right - left + 1

这是 at-most-k 类型题的常见技巧。


1️⃣4️⃣ Exactly K 问题


重要技巧

Exactly k 经常可以转化成 at most k。

exactly(k) = at_most(k) - at_most(k - 1)

Example

统计 exactly k distinct integers 的 subarrays:

def subarrays_with_k_distinct(nums, k):
    return at_most_k(nums, k) - at_most_k(nums, k - 1)

为什么正确?

最多 k 个
减去
最多 k - 1 个
等于
正好 k 个

👉 面试回答

对 exactly-k 类型的 sliding window 问题, 我经常把它转化成两个 at-most-k 问题。

公式是 exactly(k) = at_most(k) - at_most(k - 1)。

因为 at-most-k 的窗口条件通常更容易维护。


1️⃣5️⃣ Window State 设计


常见 Window State

Problem Type Window State
Sum problem current sum
Product problem current product
Duplicate problem set or frequency map
Distinct count problem frequency map
Min/max problem monotonic deque
Required character problem need/window count maps

高级工程师应该问

我需要知道当前 window 的哪些信息,
才能判断它是否 valid?

👉 面试回答

Sliding window 最重要的设计点是 window state。

对 sum problem,state 通常是 running sum。

对 duplicate 或 distinct problem,state 通常是 set 或 frequency map。

对窗口内 max/min problem,可能需要 monotonic deque。

Window state 的目标是让 validity check 足够高效。


1️⃣6️⃣ 什么时候 Sliding Window 不适用?


Negative Numbers 可能破坏单调性

例如:

Minimum subarray sum >= target

如果所有数都是 positive,sliding window 有效。

但如果有 negative numbers:

[2, -1, 2]

扩展窗口不一定增加 sum。

收缩窗口也不一定减少 sum。


非连续问题

Sliding window 只适合 contiguous range。

如果题目问的是:

subsequence
arbitrary subset

通常不适合 sliding window。


Condition 不能增量维护

如果 window condition 不能高效更新, sliding window 也不一定有帮助。


👉 面试回答

Sliding window 不是所有题都能用。

它通常要求问题是 contiguous range, 并且 window condition 可以增量维护。

如果 negative numbers 破坏了 sum 的单调性, 或者题目是 subsequence 而不是 substring/subarray, 那 sliding window 可能不是正确方法。


1️⃣7️⃣ Sliding Window vs Two Pointers


关系

Sliding window 是 two pointers 的一种特殊形式。

Two pointers: 更通用的技巧
Sliding window: 维护一个连续区间

Two Pointers 可以是:


Sliding Window 一定表示:

一个连续 window [left, right]

👉 面试回答

Sliding window 是 two pointers 的一个子类型。

Two pointers 是更大的概念。

Sliding window 特指用 left 和 right 维护一个连续区间。

它主要用于 substring 和 subarray 问题。


1️⃣8️⃣ 常见 Edge Cases


Empty Input

s = ""
nums = []

k Equals 0

k = 0

通常需要特殊处理。


k Greater Than Length

k > len(nums)

固定窗口问题中可能不存在 valid window。


Duplicate Characters

substring 问题中特别常见。


Update Timing

有的题需要:

update answer before shrinking

有的题需要:

update answer after shrinking

取决于你要找 longest 还是 minimum。


👉 面试回答

常见 edge cases 包括 empty input、k 等于 0、k 大于数组长度、duplicates、以及 update timing。

我会特别注意: 是在 window valid 时更新答案, 还是在 shrink 之后更新答案。

这通常取决于题目要 longest window 还是 minimum window。


1️⃣9️⃣ Complexity Analysis


Time Complexity

多数 sliding window 是:

O(n)

原因是:

right 从 0 到 n - 1
left 也最多从 0 到 n - 1

每个元素最多加入一次、移除一次。


Space Complexity

取决于 window state:

O(1) for running sum
O(k) or O(charset size) for frequency map
O(n) worst case for hash map

👉 面试回答

Sliding window 通常是 O(n), 因为两个 pointer 都是单向移动。

每个元素最多进入窗口一次、离开窗口一次。

Space complexity 取决于维护的 state, 比如 running sum、set、frequency map 或 deque。


2️⃣0️⃣ 常见错误


Mistake 1: 在不满足单调性的题上强行使用 Sliding Window

例如:

negative numbers in minimum subarray sum

Mistake 2: 答案更新时机错误

找 longest valid window:

make window valid first, then update answer

找 minimum valid window:

while window is valid, update answer before shrinking

Mistake 3: 忘记从 Frequency Map 删除 0 count

if count[x] == 0:
    del count[x]

Mistake 4: Infinite Loop

在 while loop 中忘记移动 left。


Mistake 5: 混淆 Fixed Window 和 Variable Window

Fixed-size:

window size is exactly k

Variable-size:

window size depends on condition

👉 面试回答

Sliding window 常见错误包括: 在不满足单调性的题上使用它、 答案更新时机错误、 frequency map 没删除 0 count、 忘记移动 left 导致 infinite loop、 以及混淆 fixed-size 和 variable-size window。

我通常通过先定义 window invariant 来避免这些问题。


2️⃣1️⃣ 标准模板


Fixed-Size Window Template

def fixed_window(nums, k):
    window_state = 0
    answer = None

    for right in range(len(nums)):
        add nums[right] to window_state

        if right >= k - 1:
            update answer

            left = right - k + 1
            remove nums[left] from window_state

    return answer

Variable-Size Longest Window Template

def longest_valid_window(nums):
    left = 0
    answer = 0

    for right in range(len(nums)):
        add(nums[right])

        while window_invalid():
            remove(nums[left])
            left += 1

        answer = max(answer, right - left + 1)

    return answer

Variable-Size Minimum Window Template

def minimum_valid_window(nums):
    left = 0
    answer = float("inf")

    for right in range(len(nums)):
        add(nums[right])

        while window_valid():
            answer = min(answer, right - left + 1)
            remove(nums[left])
            left += 1

    return answer

Count Valid Subarrays Template

def count_valid_windows(nums):
    left = 0
    answer = 0

    for right in range(len(nums)):
        add(nums[right])

        while window_invalid():
            remove(nums[left])
            left += 1

        answer += right - left + 1

    return answer

🧠 Staff-Level Answer Final


👉 面试回答完整版本

Sliding window 是一种用于 contiguous subarray 或 substring 问题的 two-pointer 技巧。

核心思想是用 left 和 right pointer 维护一个 window, 并在窗口移动时增量更新 window state。

相比暴力枚举所有 subarray 或 substring, sliding window 不会每次从头计算窗口内容。

right pointer 移动时加入一个元素, left pointer 移动时移除一个元素。

这样通常可以把 O(n²) 的解法优化成 O(n)。

我通常把 sliding window 分成 fixed-size 和 variable-size 两类。

Fixed-size window 用于题目明确要求 size 为 k 的窗口。 比如 size k 的最大 subarray sum,或者找所有长度为 len(p) 的 anagram。

Variable-size window 用于窗口大小由条件决定的题。 right pointer 负责扩展窗口。 如果窗口 invalid,就移动 left pointer 收缩窗口,直到重新 valid。

Sliding window 最关键的是定义 window state 和 invariant。

对 sum problem,state 可能是 running sum。 对 duplicate 或 distinct problem,state 通常是 set 或 frequency map。 对窗口内最大值或最小值问题,可能需要 monotonic deque。

我也会判断 sliding window 是否真的适用。 它通常要求问题是连续区间,并且 condition 可以增量维护。 如果有 negative numbers 破坏单调性,或者问题问的是 subsequence 而不是 substring/subarray, sliding window 可能不是正确方法。

复杂度通常是 O(n), 因为每个元素最多进入窗口一次、离开窗口一次。

高级工程师解释 sliding window 时, 不能只说“用 sliding window 模板”。

要讲清楚: window 代表什么, window state 是什么, window valid/invalid 的条件是什么, left 和 right 什么时候移动, 以及为什么不会跳过任何可能的答案。


⭐ Final Insight

Sliding Window 的核心不是 left/right 两个变量。

它的核心是维护一个动态 window invariant。

Senior-level 的重点是:

当前 window 表示什么, state 如何增量维护, 什么情况下 expand, 什么情况下 shrink, 以及为什么整体只需要 O(n)。

讲清楚这些,Sliding Window 就不是模板题, 而是一个可以解释、证明、扩展的算法模式。

Implement