leetcode / Sliding window / Fixed-size sliding window intro

Fixed-size sliding window

When the question is about every stretch of exactly k consecutive items, do not rebuild each stretch: slide it. One item enters, one leaves, and a running summary absorbs the difference in constant time. This page covers sums, letter counts with a "number of mismatches" counter, and set-based near-duplicate checks, and where the pattern stops working.

On this page
  1. Problem
  2. Intuition
  3. Mechanics
  4. The invariant
  5. The mismatch counter for anagrams
  6. Near-duplicates with a set
  7. Worked example
  8. Implementation
  9. Variations and edge cases
  10. Complexity and tradeoffs
  11. Problems
  12. Easy
  13. Medium
  14. Also uses this pattern
  15. Connections
  16. Read first
  17. Read next
  18. Check yourself
  19. References

Problem

"Largest sum of k consecutive numbers", "does any substring of length k match this pattern", "any repeat within k positions": each asks about all windows of one fixed width. Recomputing a window from scratch costs O(k), so n windows cost O(n·k). Neighbouring windows share k − 1 items, and the summary can be updated instead of rebuilt.

SignalPatternWhy
"Maximum (or average) of every k consecutive elements"Running sumAdd the entering value, subtract the leaving one.
"Does a permutation or anagram of p occur in s?"Window of len(p) with a count mapAn anagram has the same letter counts as p, and counts update in O(1).
"Two equal values within distance k"Set holding the last k valuesEvicting the value that falls out keeps the set equal to the window.
"Max or min of every window of size k"Window plus a monotonic dequeA running sum cannot undo a max, so a deque keeps candidates in order.
"Events in the last 300 seconds"Queue evicted by timeThe window is defined by a time span rather than a count, which makes it a queue problem.
Circular array, windows that wrapIndex modulo n, or double the arrayWrapping adds no new idea, only a different way to index.

Intuition

Think of a train passing a platform that fits exactly k carriages. Every tick, one carriage steps onto the platform and one steps off. To know the total weight on the platform you do not re-weigh the whole platform; you add the arriving carriage and subtract the departing one. The only requirement is that the summary is reversible: it must be possible to remove an item as cheaply as adding it. Sums, counts and sets are. Maximums are not.

A window of 3 sliding across 2, 1, 5, 1, 3, 2, 9, 1 The array 2, 1, 5, 1, 3, 2, 9, 1 drawn once per window position. The window sums are 8, 7, 9, 6, 14, 12. The best window starts at index 4 with sum 14. 2 1 5 1 3 2 9 1 2 1 5 1 3 2 9 1 2 1 5 1 3 2 9 1 2 1 5 1 3 2 9 1 2 1 5 1 3 2 9 1 2 1 5 1 3 2 9 1

One row per window position, sums 8, 7, 9, 6, 14, 12. Blue is the window; green marks the best one.

Mechanics

The invariant

After processing index right, the summary describes exactly nums[right − k + 1 .. right]. Each iteration performs two updates in a fixed order: fold in nums[right], then, once the window has overgrown (right ≥ k), remove nums[right − k]. After that the window has size k again. Results are read only from right ≥ k − 1, when the first full window exists. Because the update is the exact inverse of the addition, the summary never drifts away from the window's true contents.

The mismatch counter for anagrams

To test "window letter counts equal pattern letter counts" you do not compare two maps on every step, which would cost O(26) or O(k). Store for each letter the difference window − pattern, and keep one integer: how many letters have a non-zero difference. Adding or removing a letter changes one entry, and the integer changes only when that entry crosses zero. The window is an anagram exactly when the integer is 0. Each step is O(1).

Near-duplicates with a set

Keep a set of the last k values. For each new value, check the set first (a hit means a repeat within distance k), then add it, then evict the value that is now k positions behind. The order matters: if you evict before checking, you shrink the window to k − 1 and miss repeats at exactly distance k.

Mismatch counter while sliding over cbaebabacd looking for an anagram of abc For each window of 3 letters in cbaebabacd, the number of letters whose counts differ from "abc": cba has 0, bae has 2, aeb has 2, eba has 2, bab has 2, aba has 2, bac has 0, acd has 2. The counter is zero at window starts 0 and 6, which are the anagram positions. 0 cba start 0 2 bae start 1 2 aeb start 2 2 eba start 3 2 bab start 4 2 aba start 5 0 bac start 6 2 acd start 7

Bar height is how many letters still differ from the pattern. Green means zero: an anagram.

Worked example

Best sum of 3 in [2, 1, 5, 1, 3, 2, 9, 1]. Each row adds the entering value and subtracts the leaving one.

WindowEnteringLeavingSumBest
[0..2]5–88
[1..3]1278
[2..4]3199
[3..5]2569
[4..6]911414
[5..7]131214

The best is 14, found with 6 constant-time updates instead of 18 additions.

Anagrams of "abc" in cbaebabacd:

StartWindowLetters still differing
0cba0
1bae2
2aeb2
3eba2
4bab2
5aba2
6bac0
7acd2

The counter reaches zero at starts 0 and 6.

Near duplicates in [4, 7, 9, 4, 2] with k = 3:

IndexValueSet beforeRepeat?
04[]no
17[4]no
29[4, 7]no
34[4, 7, 9]yes

The repeat is found at index 3, exactly 3 positions after the first 4.

Implementation

from collections import Counter


def best_window_sum(nums, k):
    total = sum(nums[:k])
    best = total
    for right in range(k, len(nums)):
        total += nums[right] - nums[right - k]      # enter, leave
        best = max(best, total)
    return best


def anagram_starts(text, pattern):
    k = len(pattern)
    if k > len(text):
        return []
    diff = Counter(text[:k])
    diff.subtract(Counter(pattern))
    differing = sum(1 for v in diff.values() if v)
    starts = [0] if differing == 0 else []
    for right in range(k, len(text)):
        for ch, change in ((text[right], 1), (text[right - k], -1)):
            before = diff[ch]
            diff[ch] += change
            differing += (diff[ch] != 0) - (before != 0)
        if differing == 0:
            starts.append(right - k + 1)
    return starts


def contains_nearby_duplicate(nums, k):
    window = set()
    for i, x in enumerate(nums):
        if x in window:
            return True
        window.add(x)
        if len(window) > k:
            window.remove(nums[i - k])
    return False

Variations and edge cases

  • Count-based mismatch vs. map equality. Comparing two small Counters every step is simpler and fine for an alphabet of 26; the mismatch counter is the version that is O(1) per step regardless of alphabet.
  • Window maximum. A sum is reversible but a max is not: removing the leaving item may expose a new maximum. Keep a deque of indices with decreasing values (see monotonic stack).
  • Time-based windows. A hit counter or rate limiter is a window defined by time, not count; use a queue and pop entries older than the cutoff before reading the size.
  • Binary arrays and flips. Fixed windows over 0/1 data count ones with a running sum, e.g. minimum swaps to group all ones together is the best window of size (number of ones).
  • Edge cases: k larger than the array (return early), k = 1, k = 0, negative numbers (fine for fixed windows, unlike variable ones), reading results before the first full window, and evicting before checking in the set version.

Complexity and tradeoffs

  • Time: O(n) updates, each O(1); the anagram version is O(n + alphabet) to set up.
  • Space: O(1) for sums, O(alphabet) for letter counts, O(k) for the set.
  • Baseline. Recomputing each window is O(n·k). For small k the difference is invisible; for k close to n/2 it is the difference between a second and a millisecond.
  • When it fails. The summary must be reversible. If the window size is not fixed but defined by a condition, use a variable-size window; if you need the max or min, add a deque; if you need arbitrary range queries, use prefix sums.

Problems

The home problems all slide a window of a width known up front: a pattern length or a given k. The "also uses" list adds the window maximum, whose twist is the deque.

Easy

Medium

Also uses this pattern

These problems have a different home page, but this idea is part of their solution.

Connections

This is the same-direction version of two pointers. The next page lets the window's size vary, so the left edge moves by a rule instead of in lockstep. The count-map update is the idea from the counting page, and prefix sums answer the same sum-of-a-stretch question when the stretch lengths vary freely.

Check yourself

Why does the running-sum window update need both an addition and a subtraction?

After adding the entering value the window holds k + 1 items; removing the leaving one restores size k, and the sum equals the window's contents exactly. The trace needed 6 such updates.

How does the mismatch counter let you test an anagram in O(1)?

It counts how many letters have different counts in the window and the pattern. Each added or removed letter changes one count and moves the counter only when that count crosses zero, so equality is the single test "counter == 0". It is zero at starts 0 and 6 in the example.

Why can a sliding-window maximum not be done with just a running value?

Subtraction undoes a sum, but nothing undoes a max: when the largest item leaves, the new maximum is somewhere among the remaining items and was not recorded. A deque of candidates in decreasing order keeps that information.

References

  • Cormen, Leiserson, Rivest and Stein, Introduction to Algorithms: chapter 17 on amortized analysis (3rd edition), the accounting behind constant-time incremental updates.
  • Sedgewick and Wayne, Algorithms: section 5.3 on substring search, where the window over the text is the central object (Rabin-Karp updates a rolling hash exactly this way).
  • Steven Skiena, The Algorithm Design Manual: the sections on string matching.
  • Karp and Rabin, "Efficient randomized pattern-matching algorithms" (1987), the rolling-hash window.
  • The NeetCode roadmap and the Blind 75, Grind 169, NeetCode 150 and NeetCode 250 lists, used as the index of topics and problems.