Using Sliding Window Pattern
1. Understanding Window Types
| Type | Description | Use Case | Example |
|---|---|---|---|
| Fixed-size window | Window size is constant throughout the algorithm. | Find maximum sum of k elements in array. | |
| Variable-size window | Window size changes dynamically based on conditions. | Find the longest substring with at most k distinct characters. | |
| Expanding and contracting window | Window expands until condition is met, then contracts to find optimal solution. | Find the minimum window substring that contains all characters of another string. | |
2. Finding Maximum Sum Subarray
| Property | Description | Example |
|---|---|---|
| array | Input array of integers | [1, 2, 3, 4, 5] |
| k | Size of the sliding window | 3 |
| maxSum | Maximum sum of any subarray of size k | 12 |
Example: Maximum Sum Subarray of Size k
function maxSumSubarray(arr, k) {
let windowSum = arr.slice(0, k).reduce((a, b) => a + b, 0);
let maxSum = windowSum;
for (let i = k; i < arr.length; i++) {
windowSum = windowSum - arr[i - k] + arr[i];
maxSum = Math.max(maxSum, windowSum);
}
return maxSum;
}
// Example usage:
const nums = [2, 1, 5, 1, 3, 2];
const k = 3;
console.log(maxSumSubarray(nums, k)); // Output: 9
Note: The sliding window technique is efficient for finding maximum or minimum values in a fixed-size window over an array.
3. Finding Longest Substring Without Repeats
| Property | Description | Example |
|---|---|---|
| Algorithm | Sliding window technique to find the longest substring without repeating characters | int maxLength = 0; |
| Time Complexity | O(n) where n is the length of the string | for (int i = 0; i < n; i++) { ... } |
| Space Complexity | O(min(m,n)) where m is the size of the character set | Map<Character, Integer> charIndexMap = new HashMap<>(); |
Example: Sliding Window Implementation
public static int lengthOfLongestSubstring(String s) {
Map<Character, Integer> charIndexMap = new HashMap<>();
int maxLength = 0;
int left = 0;
for (int right = 0; right < s.length(); right++) {
char currentChar = s.charAt(right);
if (charIndexMap.containsKey(currentChar)) {
left = Math.max(left, charIndexMap.get(currentChar) + 1);
}
charIndexMap.put(currentChar, right);
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
Note: The sliding window adjusts the left pointer whenever a repeated character is found, ensuring all characters in the current window are unique.
4. Finding Minimum Window Substring
| Property | Description | Example |
|---|---|---|
| slidingWindow | Finds the smallest substring in s that contains all characters of t |
minWindow("ADOBECODEBANC", "ABC") // returns "BANC" |
| left, right | Pointers defining the current window in s |
left = 0; right = 0; |
| requiredChars | Map of characters in t and their counts |
{A: 1, B: 1, C: 1} |
| windowChars | Map of characters in current window and their counts | {A: 1, D: 1} |
| minLength | Length of the minimum valid window found so far | Integer.MAX_VALUE |
| minStart | Start index of the minimum valid window found so far | -1 |
Example: Sliding Window to Find Minimum Substring
function minWindow(s, t) {
const requiredChars = {};
for (let char of t) {
requiredChars[char] = (requiredChars[char] || 0) + 1;
}
let left = 0, right = 0;
let windowChars = {};
let minStart = -1;
let minLength = Infinity;
let requiredCount = Object.keys(requiredChars).length;
let formedCount = 0;
while (right < s.length) {
const rightChar = s[right];
windowChars[rightChar] = (windowChars[rightChar] || 0) + 1;
if (requiredChars[rightChar] &&
windowChars[rightChar] === requiredChars[rightChar]) {
formedCount++;
}
while (left <= right && formedCount === requiredCount) {
const leftChar = s[left];
if (right - left + 1 < minLength) {
minLength = right - left + 1;
minStart = left;
}
windowChars[leftChar]--;
if (requiredChars[leftChar] &&
windowChars[leftChar] < requiredChars[leftChar]) {
formedCount--;
}
left++;
}
right++;
}
return minStart === -1 ? "" : s.substring(minStart, minStart + minLength);
}
Note: The algorithm uses two pointers to maintain a sliding window over the string
s. It expands the window by moving the right pointer and contracts it by moving the left pointer to find the minimum valid substring.
5. Finding Permutation in String
Use the sliding window pattern to check if any permutation of a pattern string exists as a substring in a given text string.
| Property | Description | Example |
|---|---|---|
| pattern | String to find permutations of | "ab" |
| s | Input string to search in | "eidbaooo" |
| result | Boolean indicating if any permutation exists | true |
Example: Check for permutation in string
function checkInclusion(pattern, s) {
const patternCount = {};
const windowSize = pattern.length;
// Count characters in pattern
for (let char of pattern) {
patternCount[char] = (patternCount[char] || 0) + 1;
}
const windowCount = {};
let left = 0;
let matches = 0;
for (let right = 0; right < s.length; right++) {
const rightChar = s[right];
windowCount[rightChar] = (windowCount[rightChar] || 0) + 1;
// Shrink window if larger than pattern size
if (right - left + 1 > windowSize) {
const leftChar = s[left];
windowCount[leftChar]--;
if (windowCount[leftChar] === 0) {
delete windowCount[leftChar];
}
left++;
}
// Check if current window matches pattern
if (right - left + 1 === windowSize) {
let match = true;
for (let char in patternCount) {
if (!(char in windowCount) || windowCount[char] !== patternCount[char]) {
match = false;
break;
}
}
if (match) return true;
}
}
return false;
}
// Example usage
console.log(checkInclusion("ab", "eidbaooo")); // true
6. Finding Longest Substring with K Distinct Characters
| Property | Description | Example |
|---|---|---|
| Input | String and integer k (number of distinct characters allowed) | "araaci", k = 2 |
| Output | Length of the longest substring with at most k distinct characters | 4 (substring "araa") |
Example: Sliding Window Implementation
function lengthOfLongestSubstringKDistinct(s, k) {
if (s.length === 0 || k === 0) return 0;
let left = 0;
let maxLength = 0;
const charCount = new Map();
for (let right = 0; right < s.length; right++) {
const rightChar = s[right];
charCount.set(rightChar, (charCount.get(rightChar) || 0) + 1);
while (charCount.size > k) {
const leftChar = s[left];
charCount.set(leftChar, charCount.get(leftChar) - 1);
if (charCount.get(leftChar) === 0) {
charCount.delete(leftChar);
}
left++;
}
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
// Example usage:
console.log(lengthOfLongestSubstringKDistinct("araaci", 2)); // Output: 4
Note: The sliding window technique efficiently maintains a window of characters with at most k distinct characters by expanding the right pointer and shrinking the left pointer when the constraint is violated.
7. Solving Fruits into Baskets Problem
Example: Fruits into Baskets
function fruitsIntoBaskets(fruits) {
let maxLength = 0;
let windowStart = 0;
let fruitFrequency = new Map();
for (let windowEnd = 0; windowEnd < fruits.length; windowEnd++) {
const rightFruit = fruits[windowEnd];
fruitFrequency.set(rightFruit, (fruitFrequency.get(rightFruit) || 0) + 1);
while (fruitFrequency.size > 2) {
const leftFruit = fruits[windowStart];
fruitFrequency.set(leftFruit, fruitFrequency.get(leftFruit) - 1);
if (fruitFrequency.get(leftFruit) === 0) {
fruitFrequency.delete(leftFruit);
}
windowStart++;
}
maxLength = Math.max(maxLength, windowEnd - windowStart + 1);
}
return maxLength;
}
// Example usage:
const fruits = ['A', 'B', 'C', 'A', 'C'];
console.log(fruitsIntoBaskets(fruits)); // Output: 3
| Property | Description | Notes |
|---|---|---|
| fruits | Array of fruit types | Each element represents a type of fruit |
| maxLength | Maximum number of fruits that can be collected | Result of the algorithm |
| windowStart | Start index of the sliding window | Used to shrink the window when needed |
| fruitFrequency | Map storing the count of each fruit type in the current window | Used to maintain at most 2 types of fruits |
Note: The algorithm uses a sliding window approach to find the longest contiguous subarray with at most 2 unique fruit types.
8. Finding Longest Repeating Character Replacement
| Property | Description | Value |
|---|---|---|
| algorithm | Sliding window technique | Used to find the longest substring with at most k replacements |
| time complexity | Time to process the string | O(n) where n is the length of the string |
| space complexity | Space used by the algorithm | O(1) assuming fixed character set (e.g., 26 lowercase letters) |
Example: Sliding Window for Replacing Characters
function characterReplacement(s, k) {
let maxCount = 0;
let maxLength = 0;
let left = 0;
const charCount = {};
for (let right = 0; right < s.length; right++) {
const rightChar = s[right];
charCount[rightChar] = (charCount[rightChar] || 0) + 1;
maxCount = Math.max(maxCount, charCount[rightChar]);
while (right - left + 1 - maxCount > k) {
const leftChar = s[left];
charCount[leftChar]--;
left++;
}
maxLength = Math.max(maxLength, right - left + 1);
}
return maxLength;
}
// Example usage:
console.log(characterReplacement("ABAB", 2)); // Output: 4
console.log(characterReplacement("AABABBA", 1)); // Output: 4
Note: The sliding window maintains a window where the number of characters that need to be replaced is less than or equal to k. The maximum length of such a window is the answer.
9. Finding Maximum Consecutive Ones
| Property | Description | Example |
|---|---|---|
| Input | Array of integers (0s and 1s) | [1, 1, 0, 1, 1, 1] |
| Output | Maximum number of consecutive 1s | 3 |
Example: Sliding Window Approach
function findMaxConsecutiveOnes(nums) {
let maxCount = 0;
let currentCount = 0;
for (let i = 0; i < nums.length; i++) {
if (nums[i] === 1) {
currentCount++;
maxCount = Math.max(maxCount, currentCount);
} else {
currentCount = 0;
}
}
return maxCount;
}
// Example usage:
const nums = [1, 1, 0, 1, 1, 1];
console.log(findMaxConsecutiveOnes(nums)); // Output: 3
Note: This approach uses a single pass through the array, maintaining a running count of consecutive 1s and updating the maximum count whenever a longer sequence is found.
10. Finding Minimum Size Subarray Sum
Given an array of positive integers and a target sum, find the minimum length of a contiguous subarray whose sum is at least the target.
| Property | Description | Example |
|---|---|---|
| array | Input array of positive integers | [2, 3, 1, 2, 4, 3] |
| target | Target sum to achieve | 7 |
| result | Minimum length of subarray with sum >= target | 2 |
Example: Finding Minimum Size Subarray Sum
function minSubArrayLen(target, nums) {
let left = 0;
let sum = 0;
let minLength = Infinity;
for (let right = 0; right < nums.length; right++) {
sum += nums[right];
while (sum >= target) {
minLength = Math.min(minLength, right - left + 1);
sum -= nums[left];
left++;
}
}
return minLength === Infinity ? 0 : minLength;
}
// Example usage:
const nums = [2, 3, 1, 2, 4, 3];
const target = 7;
console.log(minSubArrayLen(target, nums)); // Output: 2
Note: The sliding window technique is optimal here with O(n) time complexity.
11. Finding All Anagrams in String
The Sliding Window technique can be effectively used to find all anagrams of a pattern string within a given text string. This approach leverages the concept of character frequency matching.
| Step | Description | Action |
|---|---|---|
| 1 | Initialize window size to the length of the pattern string. | Set windowSize = pattern.length |
| 2 | Create frequency map for the pattern string. | |
| 3 | Slide the window over the text string and compare character frequencies. | |
| 4 | Return all starting indices where an anagram is found. | return result; |
Example: Finding Anagrams in a String
function findAnagrams(text, pattern) {
const result = [];
const windowSize = pattern.length;
const patternMap = new Map();
const windowMap = new Map();
// Build frequency map for pattern
for (let char of pattern) {
patternMap.set(char, (patternMap.get(char) || 0) + 1);
}
// Slide window over text
for (let right = 0; right < text.length; right++) {
const rightChar = text[right];
windowMap.set(rightChar, (windowMap.get(rightChar) || 0) + 1);
if (right - left + 1 > windowSize) {
const leftChar = text[left];
windowMap.set(leftChar, windowMap.get(leftChar) - 1);
if (windowMap.get(leftChar) === 0) {
windowMap.delete(leftChar);
}
left++;
}
if (right - left + 1 === windowSize &&
areMapsEqual(windowMap, patternMap)) {
result.push(left);
}
}
return result;
}
function areMapsEqual(map1, map2) {
if (map1.size !== map2.size) return false;
for (let [key, value] of map1) {
if (map2.get(key) !== value) return false;
}
return true;
}
// Example usage:
const text = "cbaebabacd";
const pattern = "abc";
console.log(findAnagrams(text, pattern)); // Output: [0, 6]
Note: The time complexity is O(n * m) where n is the length of the text and m is the length of the pattern. Space complexity is O(m) for storing character frequencies.
12. Finding Substring with Concatenation
The "Finding Substring with Concatenation" problem involves identifying all starting indices in a string where a concatenation of all words from a given list appears in any order.
| Property | Description | Example |
|---|---|---|
| s | Input string to search for concatenated substrings | "barfoothefoobarman" |
| words | List of words to concatenate and search for | ["foo", "bar"] |
| wordLength | Length of each word in the list (assumed to be equal) | 3 |
| totalLength | Total length of all words combined | 6 |
Example: Finding Concatenated Substrings
function findSubstring(s, words) {
if (!s || !words || words.length === 0) return [];
const wordLength = words[0].length;
const totalLength = wordLength * words.length;
const wordCount = {};
const result = [];
// Count occurrences of each word
for (const word of words) {
wordCount[word] = (wordCount[word] || 0) + 1;
}
// Slide window over each possible starting position
for (let i = 0; i < wordLength; i++) {
let left = i;
let count = 0;
const currentCount = {};
for (let right = i; right <= s.length - wordLength; right += wordLength) {
const word = s.substring(right, right + wordLength);
if (wordCount[word]) {
currentCount[word] = (currentCount[word] || 0) + 1;
count++;
// Adjust window if a word appears more times than allowed
while (currentCount[word] > wordCount[word]) {
const leftWord = s.substring(left, left + wordLength);
currentCount[leftWord]--;
count--;
left += wordLength;
}
// If all words matched, record the index
if (count === words.length) {
result.push(left);
}
} else {
// Reset window if word not in list
currentCount = {};
count = 0;
left = right + wordLength;
}
}
}
return result;
}
// Example usage:
const s = "barfoothefoobarman";
const words = ["foo", "bar"];
console.log(findSubstring(s, words)); // Output: [0, 9]
Note: The algorithm uses a sliding window approach to efficiently check all possible concatenated substrings without generating every permutation explicitly.