DSA sheet · Arrays & Strings · Sliding Window pattern

Sliding Window Patterns Overview

This is the concept video that opens the sliding window series. The teacher doesn't solve one problem fully. Instead she shows how to recognise a sliding window question and splits them into four types, each with one LeetCode example: a fixed-size window, a dynamic (variable-size) window, a window with a frequency map, and a "longest window after at most k changes" window. For each type she explains the moves of the two pointers on a small example.

Why it matters: almost every "best / count of contiguous subarray or substring" question in interviews is one of these four shapes. If you can name the shape in the first minute, the code is mostly a template. The rest of this notebook (problems 2–14) is just these templates applied again and again.

This concept page uses the 9 steps adapted for a pattern video:
① what a window is → ② how to spot a sliding-window problem → ③ brute force vs window (why it's faster) → ④ fixed vs variable, side by side → ⑤–⑧ each pattern she lists (idea, tiny example, dry run, Python template) → ⑨ which sheet problems use which pattern & remember

Part 0 · Before starting

1What is a window?

A subarray is a piece of an array where the elements sit next to each other (contiguous). For a string, the same thing is called a substring. You pick a start and an end and take everything in between. You may not skip anything in the middle.

The teacher's point: a subarray is exactly what we call a window. A window of length 2 is called a "window of size 2".

index01234
nums58271[8, 2] is a window of size 2 (left = 1, right = 2)
nums58271[5, 2] is not a window (8 is skipped). That's a subsequence, a different topic.

We describe a window with two indexes: left (first cell inside) and right (last cell inside). We write it as [left..right], both ends included. Its length is right − left + 1.

Doubt: why "+1" in the length?
→ Count the cells in [1..2]: index 1 and index 2, that's 2 cells, but 2 − 1 = 1. Subtraction counts the gaps between cells, so we add one for the cell we started on. (In some later problems the right pointer has already stepped onto a bad cell, and then the length is just right − left. Those pages point it out.)

2The two moves: expand and shrink

Both pointers only ever move forward. Neither ever goes back. That one fact is why sliding window is fast (step 3 below).

In this notebook the array is drawn as boxes: yellow = inside the window, grey = already left behind (it will never come back), plain = not reached yet. The row under it shows where L and R stand.

nums58271window [1..3]
 L R 

3Why not just check every window? (brute force vs sliding)

The simple way is two loops: for every start, walk an end pointer forward and recompute the window's sum or count. There are about n²/2 windows, so this is O(n²) (or O(n·k) when the size is fixed at k).

The teacher's observation, on her fixed-size example: when you move from one window to the next, most of the cells are the same. Window 1 = [10, 12, −5, −45], window 2 = [12, −5, −45, 30]. The three cells in the middle are in both. The brute force adds them again anyway. The only real change is one cell joined (30) and one cell left (10).

So instead of recomputing, we keep the old answer and do sum = sum + new_cell − old_cell. That's O(1) per move instead of O(k), and the whole scan becomes O(n).

4How to spot a sliding-window problem

Checklist built from the four patterns she shows (plus the pattern-picking she does in the next videos):

  1. The question talks about a subarray or substring (contiguous). If it says "subsequence", it's not this pattern.
  2. It wants something about everything inside the range (sum, average, count of chars, distinct values), not just the two end values. (Only the ends matter → two pointers.)
  3. It asks for a maximum / minimum / longest / shortest / count of such windows, or "does one exist?".
  4. When you grow the window, the thing you track changes in one direction only (sum only goes up when all numbers are positive, number of distinct chars only goes up, number of zeros only goes up). This is the monotonic condition (explained in step 6).
  5. The constraints are big (n up to 10⁵ or 10⁶), so O(n²) would cross about 10⁸ operations and give TLE (Time Limit Exceeded). That's the hint that an O(n) idea exists.
words in the questionpattern
"subarray of size k", "every window of length k", "average of k elements"Pattern 1: fixed size
"longest / shortest / minimum length subarray such that…", "sum ≥ target"Pattern 2: variable size
"anagram", "permutation", "at most K distinct", "all characters appear…"Pattern 3: window + frequency map
"after at most k flips / replacements / changes, longest…"Pattern 4: longest window, bad cells ≤ k
"number of subarrays such that…"counting: count += right − left + 1 (Part E)
"exactly K distinct / exactly K odd…"atMost(K) − atMost(K−1) (Part E)
"maximum of every window of size k"fixed window + monotonic deque (Part E)

5FIXED vs VARIABLE: the most important difference

Every sliding window is one of these two. Get this clear before anything else.

FIXED-size windowVARIABLE-size window
sizealways exactly k (given in the question)changes; finding the best size is the question
how right movesone step every iterationone step every iteration
how left movesexactly one step together with right (once the first window is built); left = right − k + 1 alwaysonly when needed: zero steps on some iterations, many steps on others, driven by a condition
the loop shapebuild first k cells, then "add right, remove right − k""add right; while window is bad (or good, for shortest): remove left, left += 1"
when you record the answerafter every slide (every window is a candidate)longest: after shrinking (window is valid again) · shortest: inside the while (window is valid)
typical questionmax sum / max average / anagrams of length klongest substring without repeats, shortest subarray with sum ≥ target

Fixed, k = 3. The window moves like a frame of constant width: both ends step together.

step 1429163[0..2]
step 2429163+1 −4 → [1..3], still 3 cells
step 3429163+6 −2 → [2..4], still 3 cells

Variable, "shortest with sum ≥ 7". The width breathes: it grows while too small, shrinks while big enough.

grow231243sum 6 < 7 → keep growing
grow231243sum 8 ≥ 7 → valid, size 4 → now shrink
shrink231243sum 6 → too small again → grow
One sentence eachFixed: the size is given, so the window slides (add one, remove one, every step).
Variable: the size is the answer, so the window stretches and squeezes (right always moves, left moves only when a condition says so).

6Why sliding window needs a monotonic condition

A variable window only works if we can decide without looking back: "growing can only make it worse (or better), shrinking can only fix it". For sums that's true only when every number is positive. With negatives, adding a number can make the sum smaller and removing one can make it bigger, so the window shrinks at the wrong time and skips answers.

My own tiny example: shortest subarray with sum ≥ 5 in [1, −1, 5]. The right answer is 1 (just [5]).

  1. Add 1 → sum 1. Add −1 → sum 0. Add 5 → sum 5 ≥ 5 → record size 3.
  2. Shrink: remove 1 → sum 4 < 5 → stop shrinking.
  3. Right is at the end → answer 3. Wrong. Removing the −1 next would have pushed the sum back up to 5, but the window had already stopped.

That's why the teacher always checks the constraints for negative numbers. With negatives in a sum problem she reaches for prefix sum (counting exact sums) or Kadane's (maximum sum) instead. The fixed-size window is the exception: it never decides anything, it just slides, so negatives are fine there.

7Frequency maps in Python

Patterns 3 and 4 keep a frequency map: a dict from "item" to "how many times it is inside the window right now".

frequency map basics
from collections import Counter

freq = {}
freq['a'] = freq.get('a', 0) + 1     # add one 'a' to the window
freq['a'] -= 1                       # one 'a' leaves the window
if freq['a'] == 0:
    del freq['a']                    # drop zero counts so len(freq) = number of distinct items

Counter("abca")                      # Counter({'a': 2, 'b': 1, 'c': 1}) - builds a map in one call

The teacher does exactly this in Pattern 3: when a character leaves, "first decrement the counter, and if it hits zero, remove the key". If you leave zero counts in the map, two maps that mean the same window will compare as different ({'a':1,'e':0} != {'a':1}).


Part A · Pattern 1: fixed-size window

her example: Maximum Average Subarray I (LeetCode 643)

1The idea in simple words

Use this when the question gives you the length k and asks for the max / min / average / sum of the windows of that length. The teacher says that when you see a fixed k with max or average, you can go for the fixed window straight away.

Her example: given an array and k = 4, return the largest average among all subarrays of length 4.

2Building it from her example

She reads the first numbers as 10, 12, −5, −45, then 30 (the rest of the array isn't read out, so I'll use these five).

index01234
window 11012-5-4530sum −28, average −7
L  R 
window 21012-5-4530−28 + 30 − 10 = −8, average −2
 L  R
  1. First window: indexes 0 to 3 (0..3 is 4 cells = k). Add them up: 10 + 12 − 5 − 45 = −28. Average = −28 / 4 = −7. Keep a best variable for the maximum.
  2. Second window: start one step later, at index 1. She points out that 12, −5, −45 are in both windows. The only new thing is +30 (joins on the right) and −10 (leaves on the left).
  3. So you don't need all subarrays from scratch, only "a continuous chunk of length k", updated by one add and one subtract each step.
  4. Her pointer setup: start i (left) at 0 and j (right) at k − 1, so [i..j] is exactly k cells. Each step: add nums[j+1], subtract nums[i], move both by one.
Doubt: should I keep the max average or the max sum?
→ Every window has the same length k, so the window with the biggest sum also has the biggest average. Track the max sum and divide by k once at the end. It's faster and avoids rounding noise on every step.
Doubt: negatives are in this array. Didn't Part 0 say windows break with negatives?
→ That was about variable windows, which decide when to shrink. A fixed window never decides anything. It visits every window of size k exactly once, so it's correct for any numbers.

3Template (Python)

Pattern 1 template: fixed window (LeetCode 643)
class Solution:
    def findMaxAverage(self, nums, k):
        window = sum(nums[:k])           # first window: indexes 0..k-1
        best = window
        for right in range(k, len(nums)):
            window += nums[right]        # new cell joins on the right
            window -= nums[right - k]    # oldest cell leaves on the left
            best = max(best, window)
        return best / k                  # same length for all, so max sum = max average
linewhat it means
window = sum(nums[:k])Build the first window the slow way, once. O(k).
best = windowThe first window is a real candidate, so start the max with it (not with 0, since sums can be negative here).
for right in range(k, len(nums)):right is the cell that joins. The cell that leaves is k places behind it.
window -= nums[right - k]The window was [right−k .. right−1]. After the slide it's [right−k+1 .. right], so index right−k is the one that left.
return best / kTurn the best sum into the best average.

4Dry run (hand table)

nums = [10, 12, −5, −45, 30], k = 4.

stepright (value added)window beforesum after addingshrink? (what leaves)window afterbest sum
build0..3—−28no (building the first k)[0..3]−28
14 (30)[0..3]2yes, size would be 5: index 0 (10) leaves → −8[1..4]−8

Answer: −8 / 4 = −2.0. On LeetCode's own example [1, 12, −5, −6, 50, 3], k = 4, the same code gives 12.75.

Time O(n): the first window costs k, then n − k slides cost 1 each. Space O(1).

Remember Pattern 1Given k → build the first k, then add nums[right], subtract nums[right − k], update best. Both ends move together, every step.

Part B · Pattern 2: dynamic (variable-size) window

her example: Minimum Size Subarray Sum (LeetCode 209)

1The idea in simple words

Now no length is given. The question asks for the longest or shortest (minimum / maximum length) subarray or substring that satisfies a condition. Finding that length is the whole job.

Her description of the moves: start the pointers somewhere, keep moving right until the condition changes, note the length, then move left forward, then move right again. The size is not fixed, so you have to track it yourself.

Her example: given nums = [2, 3, 1, 2, 4, 3] and target = 7, return the minimum length of a subarray whose sum is ≥ 7. (Answer: 2, the window [4, 3].)

2Building it from her example

  1. Put both pointers (she calls them i and j) at index 0. Keep a total variable.
  2. A window is valid only when its sum is ≥ 7. While the sum is less than 7, the window can't be an answer, so move j to the right.
  3. Add 2 → 2 (less). Move j, add 3 → 5 (less). Move j, add 1 → 6 (less). Move j, add 2 → 8 ≥ 7. Now [0..3] is valid.
  4. Its length: j − i + 1 = 3 − 0 + 1 = 4. Keep a min_len variable and compare.
  5. Then (her "decrement from the left"): move i forward to try a shorter window, while it stays valid. Then move j again. Repeat until j has passed the end.
Doubt: for "shortest", why shrink as soon as it's valid?
→ Growing a valid window only makes it longer, which can never beat what we already have. The only way to find something shorter is to cut from the left. We keep cutting while the window is still valid, recording each size, and stop the moment it becomes invalid.
Doubt: what about "longest"?
→ It's the mirror. Grow while the window is valid (longer is better). When it turns invalid, shrink from the left until it's valid again, and record the size after that. Problems 3, 4, 7, 11 and 12 in this notebook are "longest". Problems 6 and 14 are "shortest".

3Templates (Python)

Pattern 2 template: shortest valid window (LeetCode 209)
class Solution:
    def minSubArrayLen(self, target, nums):
        left = 0
        total = 0
        best = float('inf')
        for right in range(len(nums)):
            total += nums[right]                 # expand
            while total >= target:               # valid -> try to make it shorter
                best = min(best, right - left + 1)
                total -= nums[left]              # shrink
                left += 1
        return 0 if best == float('inf') else best
Pattern 2 template: longest valid window (generic shape)
class Solution:
    def longestSumAtMost(self, nums, limit):
        # longest subarray with sum <= limit (all nums positive)
        left = 0
        total = 0
        best = 0
        for right in range(len(nums)):
            total += nums[right]                 # expand
            while total > limit and left <= right:   # invalid -> shrink until valid
                total -= nums[left]
                left += 1
            best = max(best, right - left + 1)   # valid now, record
        return best
linewhat it means
for right in range(len(nums)):Right moves one step every iteration, no matter what. It's the "expand" pointer.
while … :A while, not an if: one new cell may need several cells removed from the left.
best = min(best, right - left + 1)Shortest: record inside the while (the window is valid there).
best = max(best, right - left + 1)Longest: record after the while (the window is valid again there).
return 0 if best == inf …No window ever reached the target → LeetCode wants 0.

4Dry run (hand table)

nums = [2, 3, 1, 2, 4, 3], target = 7.

stepright (value added)window beforesum after addingshrink? (what leaves, why)window afterbest so far
10 (2)[0..0]2no, 2 < 7[0..0]∞
21 (3)[0..1]5no[0..1]∞
32 (1)[0..2]6no[0..2]∞
43 (2)[0..3]8yes: size 4 recorded, 2 leaves → 6 < 7, stop[1..3]4
54 (4)[1..4]10yes: size 4, 3 leaves → 7 still ≥ 7: size 3, 1 leaves → 6, stop[3..4]3
65 (3)[3..5]9yes: size 3, 2 leaves → 7: size 2, 4 leaves → 3, stop[5..5]2
step 4231243first valid window, sum 8, size 4
L  R  
step 5231243after one shrink: sum 7, size 3 recorded
  L R 
step 6231243sum 7, size 2 = answer
    LR

Time O(n): right visits each index once and left visits each index at most once, so at most 2n moves in total, even though there's a while inside a for. Space O(1).

Remember Pattern 2Right always moves. Left moves in a while. Length = right − left + 1. Shortest → record inside the while. Longest → record after it. Needs the monotonic condition (positives for sums).

Part C · Pattern 3: window + frequency map

her example: Permutation in String (LeetCode 567)

1The idea in simple words

Here the condition is about how many times each character (or number) appears in the window. So besides the two pointers we keep a hash map of counts for the window. Hints in the question: "at most K distinct characters", "all characters appear the same number of times", "anagram", "permutation".

Her example: given strings s1 and s2, return True if some permutation of s1 appears as a substring of s2.

Permutation = the same characters in any order. For "ab" the permutations are "ab" and "ba". For 1 2 3 they are 123, 132, 213, 231, 312, 321. What matters is that the characters and their counts match exactly; the order doesn't.

2Building it from her example

s1 = "ab", s2 = "eidbaooo". Answer: True, because "ba" sits inside s2.

  1. Length check first: a permutation of s1 has exactly len(s1) characters. If s1 is longer than s2 (s1 = "abc", s2 = "ab"), it can't fit → return False right away.
  2. So the window is fixed at len(s1) = 2. This pattern is a fixed window with a map instead of a sum.
  3. Why a map and not a direct comparison? Comparing "ab" to "ba" character by character says "different", but "ba" is a permutation. So compare counts: s1 has {a:1, b:1}; the window "ba" also has {a:1, b:1} → equal.
  4. Start with the first 2 chars of s2, "ei": map {e:1, i:1}. Not equal.
  5. Slide: add the new char d → {e:1, i:1, d:1}; remove the char that left, e: decrement it, and since it's now 0, delete the key → {i:1, d:1}. That's the window "id". Not equal.
  6. Keep sliding. Only s2's map changes. s1's map stays the same the whole time.

3Template (Python)

Pattern 3 template: fixed window + frequency map (LeetCode 567)
class Solution:
    def checkInclusion(self, s1, s2):
        k = len(s1)
        if k > len(s2):
            return False                         # can't fit
        need = {}
        for ch in s1:
            need[ch] = need.get(ch, 0) + 1       # s1's map never changes
        window = {}
        for right in range(len(s2)):
            ch = s2[right]
            window[ch] = window.get(ch, 0) + 1   # add the new char
            if right >= k:                       # window too long: remove the oldest
                old = s2[right - k]
                window[old] -= 1
                if window[old] == 0:
                    del window[old]              # drop zero counts
            if window == need:                   # same chars, same counts
                return True
        return False
linewhat it means
if k > len(s2): return FalseHer length check.
needCounts for s1, built once.
if right >= k:After adding, the window would have k + 1 chars, so the one at right − k must leave.
del window[old]Her "decrement, and if it's zero, remove it". Without it, {i:1, e:0} would never equal {i:1}.
window == needComparing two dicts checks every key and count. At most 26 keys for lowercase letters, so it's O(26) = O(1).

4Dry run (hand table)

s1 = "ab" → need = {a:1, b:1}. s2 = "eidbaooo", k = 2.

stepright (char added)window beforemap after addingshrink? (what leaves)window afterequal to need?
10 (e)[0..0]{e:1}no, only 1 char"e"no
21 (i)[0..1]{e:1, i:1}no, exactly 2"ei"no
32 (d)[0..2]{e:1, i:1, d:1}yes, e leaves (count 0 → deleted)"id"no
43 (b)[1..3]{i:1, d:1, b:1}yes, i leaves"db"no
54 (a)[2..4]{d:1, b:1, a:1}yes, d leaves"ba" → {b:1, a:1}yes → return True
step 3eidbaooo{i:1, d:1}
 LR     
step 5eidbaooo{b:1, a:1} = need ✓
   LR   

Time O(n · 26) = O(n). Space O(26) = O(1) for lowercase letters.

Remember Pattern 3Counts, not order → keep a map for the window. Add the new char, remove the old one (delete at zero), compare maps. The window can be fixed (anagrams, permutation) or variable (at most K distinct).

Part D · Pattern 4: longest window with at most k changes

her example: Longest Repeating Character Replacement (LeetCode 424)

1The idea in simple words

The fourth type: find the maximum length, but the window is allowed to contain a few "bad" cells, because the question lets you fix up to k of them (flip, replace, change).

Her example: s = "AABABBA", k = 1. One operation = pick any character and change it to any uppercase letter. Return the length of the longest substring with all the same letter you can get after at most k operations. Change one B to A and you get "AAAA" (length 4) → answer 4.

2Building it from her examples

Her mini example: "ABC", k = 1

  1. A: nothing to change.
  2. B: different from A. We have 1 change, so turn B into A. Changes left: 0.
  3. C: different again, but no changes left. Stop. Longest so far = 2 ("AB" → "AA").

Which letters do we change? (the greedy idea)

In a window, keep the letter that appears the most, and change all the others. Changing the rarer letters costs the fewest operations. So for a window:

changes needed = window length − (count of the most frequent letter)

Her check on "AAB" (index 0..2): length 3, max frequency 2 (A) → 3 − 2 = 1 change, which is exactly the number of B's. 1 ≤ k = 1, so the window is valid, and its length 3 is a candidate.

To know the max frequency, keep a map of counts for the window and a variable max_freq.

Doubt: she says "subtract k down to zero" when a change is used. Do we really decrease k in the code?
→ No. That's just how she narrates the mini example. In the real code, k never changes. Each step we re-check (length − max_freq) ≤ k. The window may slide past a changed letter, and then that change is "given back" automatically. If you actually decrement k, you can never get it back.
Doubt: how is this different from Pattern 2?
→ It is a variable "longest" window. What's special is the condition: "number of bad cells ≤ k", where "bad" is measured with the map (length − max count). Max Consecutive Ones III (problem 4) is the same idea with bad cell = 0.

The teacher says this video only shows the pattern, not the full code. The template below is my full version of her idea.

3Template (Python)

Pattern 4 template: longest window with at most k changes (LeetCode 424)
class Solution:
    def characterReplacement(self, s, k):
        count = {}
        left = 0
        max_freq = 0
        best = 0
        for right in range(len(s)):
            ch = s[right]
            count[ch] = count.get(ch, 0) + 1
            max_freq = max(max_freq, count[ch])      # most common letter seen in a window
            if (right - left + 1) - max_freq > k:    # needs more than k changes
                count[s[left]] -= 1                  # slide: oldest letter leaves
                left += 1
            best = max(best, right - left + 1)
        return best
linewhat it means
max_freq = max(max_freq, count[ch])Only the letter that just joined can have a new higher count, so check just that one.
(right - left + 1) - max_freq > kLetters that would need changing > changes allowed → invalid.
if (not while)We move left by just one. The window keeps its size instead of shrinking. Since we only want the longest, a window never needs to get smaller than the best we've already found. (Problem 4 explains this "never-shrinking window" trick in full.)
Doubt: after a letter leaves, max_freq might be too big. Isn't that a bug?
→ It's safe. A stale (too big) max_freq can only stop us from shrinking, which keeps the window the same size. It can never make us record a size bigger than a real valid window, because best only grows when max_freq really grows, and that comes from a real window. The tests below compare it with brute force.

4Dry run (hand table)

s = "AABABBA", k = 1.

stepright (char added)window beforemap after adding · max_freqshrink? (length − max_freq > k?)window afterbest
10 (A)[0..0]{A:1} · 11 − 1 = 0 → no[0..0]1
21 (A)[0..1]{A:2} · 20 → no[0..1]2
32 (B)[0..2]{A:2, B:1} · 23 − 2 = 1 → no[0..2]3
43 (A)[0..3]{A:3, B:1} · 34 − 3 = 1 → no[0..3]4
54 (B)[0..4]{A:3, B:2} · 35 − 3 = 2 > 1 → yes, A (index 0) leaves[1..4]4
65 (B)[1..5]{A:2, B:3} · 35 − 3 = 2 → yes, A (index 1) leaves[2..5]4
76 (A)[2..6]{A:2, B:3} · 35 − 3 = 2 → yes, B (index 2) leaves[3..6]4
step 4AABABBA"AABA": change the B → "AAAA", length 4
L  R   
step 5AABABBAslid by one, size stays 4
 L  R  

Answer 4 ✓. Time O(n), space O(26).

Remember Pattern 4Longest window where bad cells ≤ k. Here bad = length − max_freq (keep the most common letter, change the rest). k itself never changes; you re-check the condition every step.

Part E · Extra shapes used later in the sheet

The concept video stops at the four patterns above. The later problems in this sheet use three more shapes. They are summarised here (my additions, so this page works as a full reference); each one is taught properly on its own problem page.

1Counting windows: count += right − left + 1

When the question asks how many subarrays satisfy a condition that is "at most" style (sum ≤ k, product < k, at most K distinct), use the longest-window loop, and after shrinking add right − left + 1.

Why: once [left..right] is valid, every window that ends at right and starts anywhere from left to right is also valid (it's a smaller piece of a valid window). There are exactly right − left + 1 such starts. Example: valid [2..4] ends at 4 → [2..4], [3..4], [4..4] = 3 windows.

Counting template: subarrays with sum at most k (positive numbers)
class Solution:
    def countSumAtMost(self, nums, k):
        left = 0
        total = 0
        count = 0
        for right in range(len(nums)):
            total += nums[right]
            while total > k and left <= right:   # shrink until valid (or empty)
                total -= nums[left]
                left += 1
            count += right - left + 1            # all valid windows ending at right
        return count

2Exactly K = atMost(K) − atMost(K − 1)

"Exactly K" is not monotonic: a window with too few distinct values may need to grow, and one with too many must shrink, so a single window can't decide. But "at most K" is monotonic. And:

#(exactly K) = #(at most K) − #(at most K − 1)

Because the windows with at most K distinct are exactly those with ≤ K − 1 distinct plus those with exactly K.

Exactly K template: subarrays with exactly K distinct (LeetCode 992)
class Solution:
    def subarraysWithKDistinct(self, nums, k):
        return self.at_most(nums, k) - self.at_most(nums, k - 1)

    def at_most(self, nums, k):
        if k < 0:
            return 0
        freq = {}
        left = 0
        count = 0
        for right in range(len(nums)):
            x = nums[right]
            freq[x] = freq.get(x, 0) + 1
            while len(freq) > k:                 # too many distinct values
                y = nums[left]
                freq[y] -= 1
                if freq[y] == 0:
                    del freq[y]
                left += 1
            count += right - left + 1
        return count

3Fixed window + monotonic deque (maximum of every window)

For "the max of each window of size k" a sum isn't enough: when the max leaves, what's the next max? Keep a deque (double-ended queue, collections.deque) of indexes whose values are decreasing from front to back. The front is always the max of the current window.

Monotonic deque template: Sliding Window Maximum (LeetCode 239)
from collections import deque

class Solution:
    def maxSlidingWindow(self, nums, k):
        dq = deque()                     # indexes, values decreasing front -> back
        out = []
        for right in range(len(nums)):
            while dq and nums[dq[-1]] <= nums[right]:
                dq.pop()                 # smaller values can never be a max again
            dq.append(right)
            if dq[0] <= right - k:
                dq.popleft()             # front fell out of the window
            if right >= k - 1:
                out.append(nums[dq[0]])  # front = max of [right-k+1 .. right]
        return out

Part F · Revision page

#problem in this notebookwindow typewhat's trackedanswer recorded
2Maximum Subarray with Sum K (size k)fixedsumafter every slide
3Max Consecutive Onesvariable, longestrun of 1s (reset at 0)max length
4Max Consecutive Ones IIIvariable, longest (Pattern 4)number of zeros ≤ kmax length / n − left
5Subarray Product Less Than Kvariable, countingproductcount += r − l + 1
6Minimum Size Subarray Sumvariable, shortest (Pattern 2)sum ≥ targetmin length inside the while
7Fruits Into Basketsvariable, longest + map≤ 2 distinctmax length
8Subarrays with K Different Integerscounting + exactly K trickmap of distinctatMost(K) − atMost(K−1)
9Sliding Window Maximumfixed + monotonic dequedeque of indexesfront of deque, every slide
10Find All Anagrams in a Stringfixed + map (Pattern 3)char countsstart index when maps match
11Longest Substring Without Repeatingvariable, longest + map/setno char twicemax length
12Longest Substring with K Uniquevariable, longest + mapexactly K distinctmax length
13Permutation in Stringfixed + map (Pattern 3)char countsTrue when maps match
14Minimum Window Substringvariable, shortest + mapall needed chars coveredmin window inside the while
FixedVariable (longest)Variable (shortest)Counting
left moveswith right, every stepwhile invalidwhile validwhile invalid
recordafter slideafter the whileinside the whilecount += r − l + 1 after the while
negatives OK?yesnot for sumsnot for sumsnot for sums
time / spaceO(n) time; O(1) space, or O(distinct) with a map
If you remember only 5 lines 1. Window = contiguous subarray/substring = [left..right], length right − left + 1.
2. Don't recompute: add the cell that joins, remove the cell that leaves.
3. Fixed (k given): both ends move together. Variable: right always moves, left moves in a while.
4. Longest → record after shrinking. Shortest → record while shrinking. Count → add r − l + 1.
5. Variable windows need a one-way condition (positives for sums). Counts/anagrams → frequency map, delete at zero.
Mistakes to avoid ✗ using a window for a subsequence question
✗ variable window on sums with negative numbers
✗ if where you need while for shrinking (except the deliberate never-shrinking trick)
✗ starting max at 0 when window sums can be negative
✗ leaving zero counts in the map, then comparing maps
✗ actually decrementing k in Pattern 4
✗ forgetting the "s1 longer than s2" check
test it yourself (paste under the templates above)
print(Solution().findMaxAverage([1, 12, -5, -6, 50, 3], 4))   # 12.75
print(Solution().minSubArrayLen(7, [2, 3, 1, 2, 4, 3]))        # 2
print(Solution().checkInclusion("ab", "eidbaooo"))             # True
print(Solution().characterReplacement("AABABBA", 1))           # 4
print(Solution().subarraysWithKDistinct([1, 2, 1, 2, 3], 2))   # 7
print(Solution().maxSlidingWindow([1, 3, -1, -3, 5, 3, 6, 7], 3))  # [3, 3, 5, 5, 6, 7]

(Each template defines its own Solution, so run each line right after the matching template.)

Based on this video: How to identify Sliding Window patterns