Sliding Window

Mediumarrays · strings · two pointers · hash map · counting⏱ 5 min readUpdated

Maintain a contiguous range with two forward-only pointers and an incrementally updated state, so every subarray or substring question is answered in one O(n) sweep.

PatternSliding Window

Instinct Longest, shortest or count of contiguous subarrays/substrings meeting a rule? Grow a window on the right, shrink it on the left, never step back.

Memory hack Move like a caterpillar - the head stretches forward to grab, the tail pulls up only when the body breaks the rule.

At a glance

When to use it

  • The answer is about a contiguous subarray or substring
  • You want the longest, shortest or count of windows meeting a rule
  • The window has a fixed size k, or a limit like at most K distinct or K changes
  • Shrinking a valid window keeps it valid, so the rule is monotone

Key idea

Keep a window [left, right] with a running state; the right pointer adds one element, the left pointer removes one, and neither moves backwards, so each element is touched at most twice and the O(n^2) subarray scan becomes O(n).

Typical complexity

O(n) time, O(1) or O(k) space

How to spot it If the problem says "contiguous subarray" or "substring" and asks for the longest, shortest or every window of size k meeting a condition, think sliding window first.

The instinct

Brute force over subarrays recomputes the same middle elements again and again. A sliding window keeps a running state (sum, counts, distinct total) for the current range [left, right] and updates it in O(1) as elements enter on the right and leave on the left. Because both pointers only move forward, each element is added once and removed at most once: O(n) total.

The pattern works only when the rule is monotone: if a window is valid, shrinking it keeps it valid (or growing it keeps it invalid). That property is what makes "shrink until it's fine again" correct.

Recognise it

Signal in the problem Why it points here
"Contiguous subarray" / "substring" A window is exactly a contiguous range.
"Of size k", "every window of length k" Fixed window: add the new right, drop the old left.
"Longest substring without repeating characters" Variable window; shrink while the rule is broken, then record.
"At most K distinct", "at most K replacements / flips" Validity is monotone in window size, ideal for grow/shrink.
"Smallest subarray with sum ≥ target" (positive numbers) Shrink while still valid to minimise length.
"Anagram / permutation of p inside s" Fixed window of length p.length() compared with a count array.
"Minimum window containing all characters of t" Variable window plus a "how many requirements are met" counter.

Not this pattern if...

  • The array has negative numbers and you need an exact sum: adding an element no longer guarantees the sum rises, so use Prefix Sum + hash map.
  • Elements need not be contiguous (subsequence): think DP or greedy.
  • You want the maximum sum of any subarray with no size or validity constraint: that is Kadane's Running Best.
  • You need a pair from both ends of sorted data: that is Two Pointers (converging).

Memory hack

Tip

Move like a caterpillar. The head (right pointer) always stretches forward to grab the next element; the tail (left pointer) pulls up only when the body breaks the rule. A caterpillar never walks backwards, and neither do your pointers. For "at most K" problems, remember the companion rhyme: "Exactly K = at most K minus at most K-1."

Say it out loud in the interview:

  1. "What state do I keep for the window (sum, count array, distinct counter)?"
  2. "What makes the window invalid, and is that rule monotone so shrinking fixes it?"
  3. "Do I record the answer after shrinking (longest) or while shrinking (shortest)?"

How it works

Every step: add s[right] to the state; while the window is invalid, remove s[left] and advance left; then update the answer. For "shortest valid" problems, flip it: while the window is valid, record its length and then shrink.

Trace for Longest Substring Without Repeating Characters on "abcabcbb":

right char window after shrinking length best
0 a a 1 1
1 b ab 2 2
2 c abc 3 3
3 a bca (dropped a) 3 3
4 b cab (dropped b) 3 3
5 c abc (dropped c) 3 3
6 b cb (dropped a, b) 2 3
7 b b (dropped c, b) 1 3
flowchart LR
  A["right++ : add element to state"] --> B{"Window invalid?"}
  B -- yes --> C["remove nums[left], left++"]
  C --> B
  B -- no --> D["update answer with right - left + 1"]
  D --> A

In Longest Repeating Character Replacement, the window is valid while length - maxFreq <= k. In Minimum Window Substring, keep a missing counter of required characters; the window is valid when it hits zero.

Java template

java
class SlidingWindowToolkit {
    // Fixed size k: slide one step, adjust state in O(1).
    static long bestFixedWindow(int[] nums, int k) {
        long sum = 0, best = Long.MIN_VALUE;
        for (int right = 0; right < nums.length; right++) {
            sum += nums[right];                       // element enters
            if (right >= k) sum -= nums[right - k];   // element leaves
            if (right >= k - 1) best = Math.max(best, sum);
        }
        return best;
    }

    // Variable size, longest valid window (shrink while invalid, then record).
    static int longestValid(String s, int k) {
        int[] count = new int[128];
        int left = 0, distinct = 0, best = 0;
        for (int right = 0; right < s.length(); right++) {
            if (count[s.charAt(right)]++ == 0) distinct++;    // add to state
            while (distinct > k) {                            // problem-specific invalid rule
                if (--count[s.charAt(left)] == 0) distinct--; // remove from state
                left++;
            }
            best = Math.max(best, right - left + 1);
        }
        return best;
    }

    // Variable size, shortest valid window (record while valid, then shrink).
    static int shortestWithSumAtLeast(int[] nums, int target) {
        int left = 0, best = Integer.MAX_VALUE;
        long sum = 0;
        for (int right = 0; right < nums.length; right++) {
            sum += nums[right];
            while (sum >= target) {                           // valid: try to shrink
                best = Math.min(best, right - left + 1);
                sum -= nums[left++];
            }
        }
        return best == Integer.MAX_VALUE ? 0 : best;
    }
}

Complexity

  • Time O(n): right moves n times, left moves at most n times in total, even with a nested while.
  • Space O(1) for sums, or O(Σ) for a count array over the alphabet (constant for ASCII), O(k) for a hash map of distinct keys.
  • Substring with Concatenation of All Words runs one window per offset 0..wordLen-1, giving O(n · wordLen) overall.

Variations

  • Fixed window: size is given. Maximum Average Subarray I, Maximum Sum of Distinct Subarrays With Length K.
  • Fixed window with frequency match: compare counts against a pattern. Find All Anagrams in a String, Permutation in String.
  • Longest valid (shrink while invalid): Longest Substring Without Repeating Characters, Max Consecutive Ones III, Longest Repeating Character Replacement.
  • Shortest valid (shrink while valid): Minimum Size Subarray Sum, Minimum Window Substring.
  • Exactly K via at-most: atMost(K) - atMost(K - 1) counts subarrays with exactly K distinct values or K odd numbers.
  • Word-level windows: step by word length instead of by character. Substring with Concatenation of All Words.
  • Window with a monotonic deque: sliding window maximum keeps candidate indices in decreasing order.

Common mistakes

  • Using if instead of while when shrinking, so the window can stay invalid for several steps.
  • Updating the answer before the window has been repaired (longest) or after it has been broken (shortest).
  • Forgetting to undo the state when left moves (decrement counts, adjust the distinct counter).
  • Off-by-one window length: it is right - left + 1.
  • In fixed windows, recording before the first full window exists (right < k - 1).
Warning

A variable sliding window on sums only works when all numbers are non-negative. With negatives, extending the window can lower the sum, so "shrink until valid" is no longer correct. Subarray Sum Equals K is the classic trap: it needs prefix sums with a hash map, not a window.

All practice problems

10 problems on this site use this pattern, easiest first. Solve them in order and say the instinct out loud before you code.

My notes saved in this browser