Sliding Window
Overview
Sliding window algorithms process contiguous ranges without recomputing every range from scratch. Two indices describe the current window, while a small amount of state—such as a sum, product, frequency map, or distinct count—is updated as elements enter and leave.
Use a fixed-size window when every candidate range has the same length. Use a variable-size window when the right boundary expands until a constraint fails and the left boundary advances until the window becomes valid again. Most solutions run in O(n) time because each element enters and leaves the window at most once.
Templates
-
Fixed-size window
- Add
values[right]to the window state. - If
windowSize > k, removevalues[left]and incrementleft. - If
windowSize == k, evaluate the current candidate.
- Add
-
Variable-size window
- For each
right, addvalues[right]to the state. - While
windowIsInvalid, removevalues[left]and incrementleft. - Once valid, update the answer using
[left, right]. - The validity rule must be restored before moving
rightagain.
- For each
Fixed-length sliding window
public static int fixedLengthSlidingWindow(int[] nums, int k) {
int state = 0; // choose appropriate data structure
int start = 0;
int max = 0;
for (int end = 0; end < nums.length; end++) {
// extend window
// add nums[end] to state in O(1) time
if (end - start + 1 == k) {
// INVARIANT: size of the window is k here.
max = Math.max(max, state); // replace state as appropriate
// contract window
// remove nums[start] from state in O(1) time
start++;
}
}
return max;
} def fixed_length_sliding_window(nums, k):
state = # choose appropriate data structure
start = 0
max_ = 0
for end in range(len(nums)):
# extend window
# add nums[end] to state in O(1) in time
if end - start + 1 == k:
# INVARIANT: size of the window is k here.
max_ = max(max_, contents of state)
# contract window
# remove nums[start] from state in O(1) in time
start += 1
return max_ Variable-size sliding window
import java.util.HashMap;
import java.util.Map;
public static int variableLengthSlidingWindow(int[] nums) {
Map<Integer, Integer> state = new HashMap<>(); // choose appropriate data structure
int start = 0;
int max = 0;
for (int end = 0; end < nums.length; end++) {
// extend window
// add nums[end] to state in O(1) time
while (!isValid(state)) {
// repeatedly contract window until it is valid again
// remove nums[start] from state in O(1) time
start++;
}
// INVARIANT: state of current window is valid here.
max = Math.max(max, end - start + 1);
}
return max;
} def variable_length_sliding_window(nums):
state = # choose appropriate data structure
start = 0
max_ = 0
for end in range(len(nums)):
# extend window
# add nums[end] to state in O(1) in time
while state is not valid:
# repeatedly contract window until it is valid again
# remove nums[start] from state in O(1) in time
start += 1
# INVARIANT: state of current window is valid here.
max_ = max(max_, end - start + 1)
return max_ Problems
3. Longest Substring Without Repeating Characters
Find the longest substring containing no repeated character. The Java solution stores each character’s most recent index and jumps left past a duplicate in one step. The Python solution counts characters and shrinks the window until the duplicate is removed. Both approaches run in O(n) time.
import java.util.HashMap;
import java.util.Map;
class Solution {
public int lengthOfLongestSubstring(String s) {
Map<Character, Integer> lastSeen = new HashMap<>();
int left = 0;
int best = 0;
for (int right = 0; right < s.length(); right++) {
char current = s.charAt(right);
if (lastSeen.containsKey(current)) {
left = Math.max(left, lastSeen.get(current) + 1);
}
lastSeen.put(current, right);
best = Math.max(best, right - left + 1);
}
return best;
}
} from collections import defaultdict
class Solution:
def lengthOfLongestSubstring(self, s: str) -> int:
char_to_cnt = defaultdict(int)
left = 0
res = 0
for right, char in enumerate(s):
char_to_cnt[char] += 1
while char_to_cnt[char] > 1:
left_char = s[left]
char_to_cnt[left_char] -= 1
left += 1
res = max(res, right - left + 1)
return res 424. Longest Repeating Character Replacement
Return the longest substring that can be made uniform by replacing at most k characters. A window is valid when window length - highest frequency <= k. The Python solution recomputes the highest frequency from a fixed-size array, while the Java solution stores counts in a map and safely caches the largest frequency seen while expanding the window.
import java.util.HashMap;
import java.util.Map;
class Solution {
public int characterReplacement(String s, int k) {
Map<Character, Integer> freq = new HashMap<>();
int res = 0;
int start = 0;
int maxCount = 0; // count of the most frequent char in the window
for (int end = 0; end < s.length(); end++) {
char c = s.charAt(end);
freq.put(c, freq.getOrDefault(c, 0) + 1);
maxCount = Math.max(maxCount, freq.get(c));
while (end - start + 1 - maxCount > k) {
char startChar = s.charAt(start);
freq.put(startChar, freq.get(startChar)-1);
start++;
}
res = Math.max(res, end - start + 1);
}
return res;
}
} class Solution:
def characterReplacement(self, s: str, k: int) -> int:
counts = [0] * 26
res = 0
left = 0
for right, char in enumerate(s):
counts[ord(char)-ord('A')] += 1
while right - left + 1 - max(counts) > k:
counts[ord(s[left])-ord('A')] -= 1
left += 1
res = max(res, right - left + 1)
return res 567. Permutation in String
Determine whether s2 contains a permutation of s1. Maintain a fixed-size window equal to s1.length() and compare its character counts with the required counts. Python uses Counter objects, while Java uses two arrays for the 26 lowercase letters. Both approaches run in O(n) time.
import java.util.Arrays;
class Solution {
public boolean checkInclusion(String s1, String s2) {
if (s1.length() > s2.length()) {
return false;
}
int[] required = new int[26];
int[] window = new int[26];
for (int i = 0; i < s1.length(); i++) {
required[s1.charAt(i) - 'a']++;
window[s2.charAt(i) - 'a']++;
}
if (Arrays.equals(required, window)) {
return true;
}
for (int right = s1.length(); right < s2.length(); right++) {
window[s2.charAt(right) - 'a']++;
window[s2.charAt(right - s1.length()) - 'a']--;
if (Arrays.equals(required, window)) {
return true;
}
}
return false;
}
} from collections import Counter
class Solution:
def checkInclusion(self, s1: str, s2: str) -> bool:
N1, N2 = len(s1), len(s2)
if N1 > N2:
return False
counter1 = Counter(s1)
counter2 = Counter(s2[:N1])
if counter1 == counter2:
return True
for idx in range(N1, N2):
counter2[s2[idx]] += 1
counter2[s2[idx-N1]] -= 1
if counter2[s2[idx-N1]] == 0:
del counter2[s2[idx-N1]]
if counter1 == counter2:
return True
return False 713. Subarray Product Less Than K
Count contiguous subarrays whose product is smaller than k. Because all values are positive, expanding the window can only increase the product; shrink until it is valid, then every subarray ending at right and starting between left and right is valid.
class Solution {
public int numSubarrayProductLessThanK(int[] nums, int k) {
int res = 0;
int prod = 1;
int left = 0;
for (int right = 0; right < nums.length; right++){
prod = prod * nums[right];
while (left <= right && prod >= k){
prod = prod / nums[left];
left += 1;
}
res += (right-left+1);
}
return res;
}
} from typing import List
class Solution:
def numSubarrayProductLessThanK(self, nums: List[int], k: int) -> int:
res = 0
left = 0
prod = 1
for right in range(len(nums)):
prod = prod * nums[right]
while left <= right and prod >= k:
prod = prod // nums[left]
left += 1
res += (right - left + 1)
return res 2461. Maximum Sum of Distinct Subarrays With Length K
Find the maximum sum among length-k subarrays whose elements are all distinct. Maintain the window sum and a frequency map; after removing excess elements, evaluate the sum only when the window has size k and the map also contains k keys.
import java.util.HashMap;
import java.util.Map;
class Solution {
public long maximumSubarraySum(int[] nums, int k) {
Map<Integer, Integer> counts = new HashMap<>();
long sum = 0;
long best = 0;
int left = 0;
for (int right = 0; right < nums.length; right++) {
counts.merge(nums[right], 1, Integer::sum);
sum += nums[right];
if (right - left + 1 > k) {
int removed = nums[left++];
sum -= removed;
counts.compute(removed, (key, count) ->
count == 1 ? null : count - 1
);
}
if (right - left + 1 == k && counts.size() == k) {
best = Math.max(best, sum);
}
}
return best;
}
} from typing import List
class Solution:
def maximumSubarraySum(self, nums: List[int], k: int) -> int:
num_to_count = {}
curr_sum = 0
max_sum = 0 # positive number only according to Constraints
for idx, num in enumerate(nums):
# process current num
num_to_count[num] = num_to_count.get(num, 0) + 1
curr_sum += num
# eject out of window num
if idx >= k:
prev_num = nums[idx-k]
num_to_count[prev_num] -= 1
curr_sum -= prev_num
if num_to_count[prev_num] == 0:
del num_to_count[prev_num]
#check uniqueness and update max_sum
if len(num_to_count) == k:
max_sum = max(max_sum, curr_sum)
return max_sum