FirstHack Learn
Log in Sign up free
Lessons in this course 0/6 All courses DSA Interview Patterns

Placements

Progress0 / 6 lessons
  1. 1. Two pointers
  2. 2. Sliding window
  3. 3. Prefix sums
  4. 4. Hashing for frequency problems
  5. 5. Binary search on the answer
  6. 6. Stacks and next greater element

Courses › DSA Interview Patterns

Sliding window

Reuse the previous window instead of recomputing it - contiguous subarray problems in O(n).

12 min read · Lesson 2 of 6 · Free

The pattern

A window is a contiguous stretch [left, right] of the array or string. You extend it on the right, and when it becomes invalid you shrink it from the left. You never move left backwards.

The insight: moving the window by one position changes only two elements - one enters, one leaves. So the answer for the new window can be computed from the old one in O(1) instead of rescanning it in O(k).

Recognise it from: contiguous, subarray, substring, consecutive, longest/shortest ... such that.

Warm-up: fixed size window

Maximum sum of any k consecutive elements.

Brute force recomputes each window: O(n x k).

C++
#include <iostream>
#include <vector>
#include <unordered_map>
#include <algorithm>
using namespace std;

int max_sum_k(const vector<int> &a, int k) {
    int n = a.size();
    if (n < k) return 0;

    int window = 0;
    for (int i = 0; i < k; i++) window += a[i];

    int best = window;
    for (int i = k; i < n; i++) {
        window += a[i] - a[i - k];      // add the new, remove the old
        best = max(best, window);
    }
    return best;
}

int main() {
    vector<int> a = {2, 1, 5, 1, 3, 2};
    cout << max_sum_k(a, 3) << "\n";     // 9  (5 + 1 + 3)
    return 0;
}

The single line window += a[i] - a[i-k] is the whole pattern. O(n) time, O(1) space.

The real problem: variable size window

Find the length of the longest substring with no repeating characters.

"abcabcbb" -> 3, from "abc" "bbbbb" -> 1 "pwwkew" -> 3, from "wke"

Step 1: brute force

Check every substring for duplicates: O(n^3), or O(n^2) with a set per start position. For a 100,000-character string that is far too slow.

Step 2: think in windows

Keep a window that contains no repeats. Extend right one character at a time. When the new character is already inside the window, the window is invalid - so move left forward until it is valid again.

Trace "pwwkew":

right char window before action window after best
0 p "" add "p" 1
1 w "p" add "pw" 2
2 w "pw" duplicate, move left past the old w "w" 2
3 k "w" add "wk" 2
4 e "wk" add "wke" 3
5 w "wke" duplicate, move left past the old w "kew" 3

Answer 3.

Step 3: the code

The trick is storing the last index of each character, so left can jump straight there instead of stepping one at a time.

C++
int longest_unique(const string &s) {
    unordered_map<char, int> last_seen;   // char -> last index it appeared
    int left = 0;
    int best = 0;

    for (int right = 0; right < (int)s.size(); right++) {
        char c = s[right];
        if (last_seen.count(c) && last_seen[c] >= left) {
            left = last_seen[c] + 1;      // jump past the earlier copy
        }
        last_seen[c] = right;
        best = max(best, right - left + 1);
    }
    return best;
}

int main() {
    cout << longest_unique("abcabcbb") << "\n";   // 3
    cout << longest_unique("bbbbb") << "\n";      // 1
    cout << longest_unique("pwwkew") << "\n";     // 3
    cout << longest_unique("") << "\n";           // 0
    return 0;
}

O(n) time - right moves n times, left only ever moves forward. O(min(n, alphabet)) space.

⚠️

The condition last_seen[c] >= left is the line everyone forgets. Without it, a character seen long ago - already outside the current window - drags left backwards, and the window becomes wrong. Test with "abba": the correct answer is 2, and dropping that check gives 3.

Trace "abba" to see it. At right = 3 the character is 'a', last seen at index 0. But left is already 2. Without the check, left would be set back to 1, re-admitting 'b' and reporting a window of length 3 containing two b's.

The third shape: shrink to the minimum

Smallest subarray whose sum is at least S.

C++
int min_subarray_len(const vector<int> &a, int S) {
    int n = a.size();
    int left = 0, sum = 0, best = n + 1;

    for (int right = 0; right < n; right++) {
        sum += a[right];
        while (sum >= S) {                    // valid - try to shrink
            best = min(best, right - left + 1);
            sum -= a[left];
            left++;
        }
    }
    return best == n + 1 ? 0 : best;
}

int main() {
    vector<int> a = {2, 3, 1, 2, 4, 3};
    cout << min_subarray_len(a, 7) << "\n";   // 2  ({4, 3})
    return 0;
}

The while looks like it makes this O(n^2). It does not: left increases at most n times across the entire run, so the total work is O(n). Being able to explain that is worth as much as the code.

ℹ️

Note the difference between the two variable-size versions. For a longest answer you record the best after restoring validity. For a shortest answer you record it while the window is still valid, then shrink. Getting these the wrong way round is the usual source of off-by-one answers.

Practice, in this order

  1. Longest substring with at most k distinct characters.
  2. Permutation in string - fixed window plus a frequency count.
  3. Maximum of every window of size k, using a deque.
  4. Minimum window substring - the hardest common one, and it is this exact

shrink pattern with a character count.