Sliding Window 101
Fundamental · 4 chapters · 8 min read · Answer every 'best contiguous subarray' question by growing and shrinking one window.
Practise this in the app All guides
What a sliding window is
The idea
A sliding window is a contiguous range [left, right] of an array or string that you move from left to right while keeping a small summary of what is inside: a running sum, a count of zeros, or a frequency map of characters. Instead of recomputing that summary for every one of the O(n²) subarrays, you update it in O(1) when an element enters on the right or leaves on the left. Each element enters once and leaves once, so the whole scan is O(n).
Two kinds of window
- Fixed size. The window always has length
k. Add the new element, remove the one that fell off, record the answer. - Variable size. The window grows on the right until it breaks (or meets) a condition, then shrinks from the left until it is valid again. The answer is the best window seen.
Signals in a problem statement
- "Contiguous subarray" or "substring" (not subsequence).
- "Longest", "shortest", "maximum", "minimum" length or sum.
- "At most k" changes, distinct characters, zeros you may flip.
- "Average of every window of size k".
- "Smallest substring containing all characters of another string".
When it does not work
The variable window relies on monotonic validity: if a window is valid, shrinking it keeps it valid (or growing it keeps it invalid), so the left pointer never needs to move backwards. With negative numbers in a "sum at least k" question this breaks, because adding an element can lower the sum. In that case reach for prefix sums with a hash map or a monotonic deque instead. Asking "are all numbers non-negative?" is a smart clarifying question that interviewers notice.
Core patterns with templates
1. Fixed-size window
def max_average(nums, k):
window = sum(nums[:k])
best = window
for i in range(k, len(nums)):
window += nums[i] - nums[i - k]
best = max(best, window)
return best / k
2. Longest valid window (grow, then shrink while invalid)
Longest run of ones if you may flip at most k zeros:
def longest_ones(nums, k):
left = zeros = best = 0
for right, x in enumerate(nums):
zeros += x == 0
while zeros > k:
zeros -= nums[left] == 0
left += 1
best = max(best, right - left + 1)
return best
3. Shortest valid window (grow until valid, then shrink while still valid)
Smallest length with sum at least target (all numbers positive):
def min_subarray_len(target, nums):
left = total = 0
best = float("inf")
for right, x in enumerate(nums):
total += x
while total >= target:
best = min(best, right - left + 1)
total -= nums[left]
left += 1
return 0 if best == float("inf") else best
Example: target = 7, nums = [2,3,1,2,4,3]. The window [4,3] at the end has length 2, the answer.
4. Window with a frequency map
For "smallest substring of s containing every character of t", keep need = Counter(t) and a count missing = len(t). When s[right] is still needed, decrement missing. When missing == 0, record the window and shrink from the left, giving characters back to need and incrementing missing when a needed character leaves.
The difference between pattern 2 and pattern 3 is where you record the answer: after the shrink loop for "longest", inside it for "shortest".
Complexity, mistakes and edge cases
Complexity
Every element is added once and removed at most once, so both window types run in O(n) time, even with a while inside the for. Space is O(1) for sums and counts, or O(alphabet) for a frequency map.
Common mistakes
- Recording the answer in the wrong place. Longest-window answers go after shrinking; shortest-window answers go inside the shrink loop.
- Window length off by one. With inclusive
leftandrightthe length isright - left + 1. - Using a window on data with negatives when validity is not monotonic. Check the constraints first.
- Forgetting the "no answer" case. Return 0 or an empty string when no window ever became valid, instead of
inf. - Shrinking with
ifinstead ofwhile. Usually one step is not enough to restore validity. (There is a well-known trick for longest-window problems where anifworks because the window never shrinks below the best size, but use it only if you can explain it.) - Comparing whole maps every step. Checking
window_counts == needcosts O(alphabet) per step; maintain a singlemissingorformedcounter instead.
Edge cases
k larger than the array, k = 0, empty string t, t longer than s, all elements satisfying the condition, none satisfying it, repeated characters in t (you need each copy), and uppercase versus lowercase letters.
Checking your loop by hand
Dry-run with a five-element example and write the window after each step as [left..right] summary. If left ever exceeds right + 1, or the summary disagrees with the actual window contents, you have found your bug before the interviewer does.
Variations and practise
How interviewers twist it
- "At most k distinct characters". Keep a counter of characters in the window; shrink while
len(counter) > k, deleting keys whose count drops to zero. - "Exactly k". Compute
at_most(k) - at_most(k - 1). This converts an awkward condition into two easy windows. - Permutation or anagram inside a string. A fixed window of size
len(p)with a count comparison. - Maximum in every window. A monotonic deque keeps candidates in decreasing order; the front is the maximum. This is the bridge to the Stack & Queue card.
- Longest substring without repeating characters. Store the last index of each character and jump
leftdirectly past the previous occurrence. - Streaming data. If elements arrive one at a time, a fixed window is just a queue of the last
kitems plus a running summary.
Talking about it in the interview
State the invariant: "the window always contains at most k zeros" or "missing is the number of characters of t still not covered". Then the code is just maintaining that invariant on every add and remove.
Practise
The problems below include a fixed-size average, two variable windows (one longest, one shortest), and the classic frequency-map window that appears in many product-company interviews. For each, decide first whether you are looking for the longest or the shortest window, because that decides where the answer is recorded.
Practise
Easy
- 805. Maximum Average Subarray I
- 806. Minimum Recolors to Get K Consecutive Black Blocks
- 811. Rolling Window Totals
- 815. Weekly Step Score
- 816. Blocks With Exact Variety
- 817. Vowel-Rich Segment
- 818. Longest Rising Run
- 819. Longest Gentle Stretch
- 820. Frost Alert Windows
- 821. Variety Per Window
- 822. Circular Forward Sums
- 823. Digit Windows That Divide
- 824. Tightest Group of K
- 825. Alternating Parity Stretch
- 826. Uniform Pieces Count
- 827. First Free Seat Block
- 828. Shortest Span With Two Letters
- 829. Most Common Window Sum
- 831. Steepest K-Day Drop
- 832. Twice-Capped Substring
- 833. Majority Ones Windows
- 834. Rising Windows Count
- 835. Zero XOR Windows
- 836. Spike Over Trailing Average
- 837. Max Bit Window Value
- 838. Longest Stretch With Few Vowels
- 839. Longest Affordable Stretch
- 840. Busiest Time Span
- 841. Even Split Binary Pieces
- 842. Balanced Halves Windows
- 843. Mirror Blocks Count
- 844. Always-Present Letter Window
- 845. Best Row Strip
- 846. Positive Product Windows
- 847. Shortest Echo
- 849. Adjacent Block Gap
- 850. Stable Centers
- 851. Covering Blocks
- 852. Best Node Run
- 853. Broken Keys Typewriter
- 854. Dominated Blocks
- 855. Closest Window Average
- 937. One Stroke Flip
Medium
- 807. Max Consecutive Ones III
- 808. Minimum Size Subarray Sum
- 812. Fresh Letters Stretch
- 813. Repaint for the Longest Run
- 814. Limited Variety Substring
- 830. Count Distinct Blocks
- 856. Shuffled Pattern Positions
- 857. Full Trio Substrings
- 858. Low Product Runs
- 859. Window Peaks
- 860. Calm Stretch Within Limit
- 861. Tight Spread Subarrays
- 862. Focus Burst Bonus
- 863. Pick From Both Ends
- 864. Trim Ends to Target
- 865. Unique Run Score
- 866. Lightest Beautiful Substring
- 867. Equalize With Boosts
- 868. Repeated Signal Blocks
- 869. Every Bit Code Present
- 870. One Stutter Substring
- 871. Vowel Parade
- 872. Alternate With Rotations
- 873. Odd Quota Subarrays
- 874. Every Letter K Times
- 875. Zigzag Stretch
- 876. Max-Heavy Subarrays
- 877. Full Variety Subarrays
- 878. Equal Run After Deletions
- 879. Varied Best Window
- 880. Near-Twin Segment
- 881. Harvest From Both Ends
- 882. Bounded Length Best Sum
- 883. Shortest OR Reaching Target
- 884. Disjoint Bits Stretch
- 885. Compass Rebalance
- 886. Dominant Letter Substrings
- 887. Longest Hill Stretch
- 888. Tarp Over Garden Beds
- 889. All Vowels With K Consonants
- 890. K Nearest Readings
- 891. Popular Short Substring
- 892. One Swap Longest Run
- 893. Near Duplicates in Range
- 894. Hopscotch Max Score
- 895. Locate the Hashed Block
- 896. Kth Smallest Stretch Total
- 897. Looping Playlist Target
- 898. Window Decrements to Zero
- 899. Negative Rank Per Window
- 900. Distinct Limited Divisible Subarrays
- 901. Consecutive Power Windows
- 902. Best Plot Square
- 903. Fewest Elements for Variety
- 904. Permutation Windows
- 905. Reschedule for Free Time
- 906. Burst Detector
- 907. Count Fresh Substrings
- 908. Round Table Seating
- 909. Mode Per Window
- 910. Close Letters Stretch
- 911. Anagram Class Count
- 912. Gain Within Days
- 913. Trimmed Window Best
- 914. Two Budgets Stretch
- 915. Sum in Range Count
- 916. Bounded Peak Subarrays
- 917. Longest Shared Run
- 918. Best Alignment With Mismatches
- 919. Relaxed Binary Substrings
- 920. Frequent Values Per Window
- 921. Dry Season Stretch
- 922. Close Pair Count
- 923. Closest Stretch Total
- 924. Fair Ratio Stretch
- 925. Even Step Slices
- 926. Balanced Sample Substring
- 927. Distinct Tile Squares
- 928. Consecutive Value Subarrays
- 929. Stepping Stones Reach
- 930. Pair-Rich Subarrays
- 931. Rolling Anomaly Count
- 932. Two Net Catch
- 933. Richest Limited Variety
- 934. Common K Blocks
- 935. Equal Sum Window Pairs
- 936. Few Peaks Stretch
- 938. Spaced Repeats Stretch
- 939. Volatile Weeks
- 958. Wildcard Shuffle Windows
- 970. Square Peak Map
- 975. Always Varied Length
- 976. First Big Swing
- 977. Cut to Few Colors
- 978. Most Colorful Tile Square
Hard
- 809. Minimum Window Substring
- 810. Subarrays with K Different Integers
- 848. Median Per Window
- 940. Window Leveling Cost
- 941. Shortest Reach With Losses
- 942. Window Flip Switches
- 943. Exact Extremes Subarrays
- 944. Shortest Ordered Cover
- 945. Make It Continuous
- 946. Orchard Walk
- 947. Robot Fleet Budget
- 948. Best Long Average
- 949. Three Best Windows
- 950. Longest Echoed Substring
- 951. Narrowest Shared Range
- 952. Top Frequency Sums
- 953. Closest Pair Per Window
- 954. Signal Tower Pairing
- 955. Spaced Picks Sum
- 956. Relaxed Binary Range Queries
- 957. Ranked Value Per Window
- 959. No Gap Stretch
- 960. Widest Strong Span
- 961. Ones-Dominant Substrings
- 962. Non-Decreasing After Raises
- 963. Pebbles to Consecutive
- 964. Split Cost With Reach
- 965. Leveling Frequency Score
- 966. Trimmed Rolling Average
- 967. Two-Tone Repaint
- 968. Almost Sorted Stretches
- 969. Circular Shortest Reach
- 971. Majority Ruled Stretch
- 972. Dark Gap Day
- 973. Steady Performance Span
- 974. Window Inversions