Module F-9·14 min read

Expanding and contracting windows for substring/subarray constraint problems, without recomputation.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

Module F-9 — Sliding Window Pattern

What this module covers: Module F-7's moving average already hinted at this: instead of recomputing a result from scratch for every possible subarray or substring, maintain a "window" and adjust it incrementally as it moves. This module formalizes that into two variants — a fixed-size window and a variable-size window that expands and contracts based on a constraint — both of which turn an O(n × k) or O(n²) brute force into O(n).


The Problem: Recomputing From Scratch Is Wasteful

Say you need the maximum sum of any 3 consecutive elements in an array. The direct approach checks every possible window of size 3, summing each one independently:

typescript

Every window shares k - 1 elements with the window right before it — only one element enters and one leaves as the window slides forward by one position. Recomputing the full sum from scratch throws away that overlap and redoes work you already did one step ago.


Fixed-Size Window: Maintain a Running Total

The fix mirrors Module F-7's moving average exactly: keep a running sum, add the new element entering the window, subtract the element leaving it.

typescript

The first window is summed once, up front. Every window after that costs exactly one addition and one subtraction, regardless of k — the window "slides" one position at a time instead of being recomputed. This is the same window-shift math as the queue's moving average in Module F-7, applied to arrays directly instead of a live stream.

Analogy: A sliding window is a physical picture frame moving across a long mural, one step at a time. You don't repaint what's inside the frame every time you move it — you just note what came into view on one edge and what fell out of view on the other.


Variable-Size Window: Expand and Contract on a Constraint

Not every problem has a fixed window size — often the window needs to grow while some condition holds, and shrink the moment it's violated. The classic example: the longest substring with no repeating characters.

typescript

right only ever moves forward, one step per outer-loop iteration. left also only ever moves forward, and — this is the part worth checking carefully — across the entire run, left can advance at most s.length times in total, even though it's inside a while loop nested inside the for. That's the same "bounded total work across the whole run, despite looking nested" reasoning from the monotonic stack in Module F-6: neither pointer ever moves backward, so the combined work is O(n), not O(n²).

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.