Sliding Window
Compute over every contiguous window without recomputing the overlap. Expand with the right edge; when it violates a constraint, shrink from the left. Turns O(N·K) brute force into O(N).
windowEnd element; while the window is invalid, subtract the outgoing windowStart element and advance start.let windowStart = 0, best = 0; const freq = {}; for (let windowEnd = 0; windowEnd < s.length; windowEnd++) { const right = s[windowEnd]; freq[right] = (freq[right] || 0) + 1; // grow window while (/* window is invalid */ Object.keys(freq).length > K) { const left = s[windowStart]; freq[left]--; // shrink from left if (freq[left] === 0) delete freq[left]; windowStart++; } best = Math.max(best, windowEnd - windowStart + 1); } return best;
easyMaximum Sum Subarray of Size KFixed window: add right, at size k record max, subtract left.▸
function maxSubarraySumOfSizeK(arr, k) {
let windowSum = 0, windowStart = 0, maxSum = 0;
for (let windowEnd = 0; windowEnd < arr.length; windowEnd++) {
windowSum += arr[windowEnd];
if (windowEnd >= k - 1) {
maxSum = Math.max(maxSum, windowSum);
windowSum -= arr[windowStart++];
}
}
return maxSum;
}easySmallest Subarray with Sum ≥ SVariable window: shrink while sum ≥ S, track min length.▸
function smallestSubarrayWithSum(arr, s) {
let windowSum = 0, windowStart = 0, minLen = Infinity;
for (let windowEnd = 0; windowEnd < arr.length; windowEnd++) {
windowSum += arr[windowEnd];
while (windowSum >= s) {
minLen = Math.min(minLen, windowEnd - windowStart + 1);
windowSum -= arr[windowStart++];
}
}
return minLen === Infinity ? 0 : minLen;
}medLongest Substring with K DistinctShrink while map size > K. Fruits-into-Baskets is this with K=2.▸
function longestSubstringKDistinct(str, k) {
let windowStart = 0, maxLen = 0;
const freq = {};
for (let windowEnd = 0; windowEnd < str.length; windowEnd++) {
const right = str[windowEnd];
freq[right] = (freq[right] || 0) + 1;
while (Object.keys(freq).length > k) {
const left = str[windowStart++];
if (--freq[left] === 0) delete freq[left];
}
maxLen = Math.max(maxLen, windowEnd - windowStart + 1);
}
return maxLen;
}
function fruitsIntoBaskets(fruits) {
return longestSubstringKDistinct(fruits.join(''), 2);
}hardNo-repeat SubstringMap char → last index; jump start to max(start, last+1).▸
max(start, last+1).function longestNoRepeatSubstring(str) {
let windowStart = 0, maxLen = 0;
const lastIndex = {};
for (let windowEnd = 0; windowEnd < str.length; windowEnd++) {
const right = str[windowEnd];
if (right in lastIndex) {
windowStart = Math.max(windowStart, lastIndex[right] + 1);
}
lastIndex[right] = windowEnd;
maxLen = Math.max(maxLen, windowEnd - windowStart + 1);
}
return maxLen;
}hardLongest Substring after K ReplacementsTrack maxRepeat; shrink when windowLen − maxRepeat > k.▸
maxRepeat; shrink when windowLen − maxRepeat > k.function longestSubstringAfterReplacement(str, k) {
let windowStart = 0, maxLen = 0, maxRepeat = 0;
const freq = {};
for (let windowEnd = 0; windowEnd < str.length; windowEnd++) {
const right = str[windowEnd];
freq[right] = (freq[right] || 0) + 1;
maxRepeat = Math.max(maxRepeat, freq[right]);
// window size - most-frequent-letter count = letters we must replace
if (windowEnd - windowStart + 1 - maxRepeat > k) {
freq[str[windowStart++]]--;
}
maxLen = Math.max(maxLen, windowEnd - windowStart + 1);
}
return maxLen;
}
function longestOnesAfterReplacement(arr, k) {
let windowStart = 0, maxLen = 0, maxOnes = 0;
for (let windowEnd = 0; windowEnd < arr.length; windowEnd++) {
if (arr[windowEnd] === 1) maxOnes++;
if (windowEnd - windowStart + 1 - maxOnes > k) {
if (arr[windowStart++] === 1) maxOnes--;
}
maxLen = Math.max(maxLen, windowEnd - windowStart + 1);
}
return maxLen;
}hardPermutation · Anagrams · Min Window SubstringPattern frequency map + matched counter.▸
matched counter.function findPermutation(str, pattern) {
const need = {};
for (const c of pattern) need[c] = (need[c] || 0) + 1;
let windowStart = 0, matched = 0;
for (let windowEnd = 0; windowEnd < str.length; windowEnd++) {
const right = str[windowEnd];
if (right in need && --need[right] === 0) matched++;
if (matched === Object.keys(need).length) return true;
if (windowEnd >= pattern.length - 1) {
const left = str[windowStart++];
if (left in need && need[left]++ === 0) matched--;
}
}
return false;
}
function findAnagrams(str, pattern) {
const need = {};
for (const c of pattern) need[c] = (need[c] || 0) + 1;
const result = [];
let windowStart = 0, matched = 0;
for (let windowEnd = 0; windowEnd < str.length; windowEnd++) {
const right = str[windowEnd];
if (right in need && --need[right] === 0) matched++;
if (matched === Object.keys(need).length) result.push(windowStart);
if (windowEnd >= pattern.length - 1) {
const left = str[windowStart++];
if (left in need && need[left]++ === 0) matched--;
}
}
return result;
}
function minWindowSubstring(str, pattern) {
const need = {};
for (const c of pattern) need[c] = (need[c] || 0) + 1;
let windowStart = 0, matched = 0, minLen = Infinity, subStart = 0;
for (let windowEnd = 0; windowEnd < str.length; windowEnd++) {
const right = str[windowEnd];
if (right in need && --need[right] >= 0) matched++;
while (matched === pattern.length) {
if (windowEnd - windowStart + 1 < minLen) {
minLen = windowEnd - windowStart + 1;
subStart = windowStart;
}
const left = str[windowStart++];
if (left in need && need[left]++ === 0) matched--;
}
}
return minLen === Infinity ? '' : str.substring(subStart, subStart + minLen);
}