Advanced Sliding Window Patterns
Level 2.5: Interview Patterns - Master pattern recognition for variable-size windows and complex constraints
Advanced Sliding Window Pattern Recognition
🧭 When to Use This Pattern - Checklist
Pattern Recognition Checklist
✅ Use Advanced Sliding Window When:
- 🎯 Contiguous Elements Required: Problem asks for subarray/substring (not subsequence)
- 📊 Condition-Based Window Size: Window expands/contracts based on some condition
- 🔄 Frequency Constraints: "At most K distinct", "contains all characters", "permutation"
- 📈 Optimization Goal: "Longest/shortest subarray satisfying condition"
- ⚡ Linear Time Possible: Each element can be processed in amortized O(1) time
❌ Don't Use Sliding Window When:
- 🚫 Non-contiguous Elements: Subsequence problems, picking elements with gaps
- 🚫 Global Dependencies: Solution depends on entire array knowledge
- 🚫 Complex State Transitions: DP with overlapping subproblems
- 🚫 Structural Relationships: Tree/graph traversal, parent-child dependencies
🔍 Decision Flowchart:
┌─────────────────────────────────┐ │ Problem Analysis Start │ └─────────────┬───────────────────┘ │ ┌─────────────▼───────────────────┐ │ Does problem involve finding │ │ subarray/substring with │ │ specific condition? │ └─────────────┬───────────────────┘ │ YES ┌─────────────▼───────────────────┐ │ Is the window size variable │ │ based on condition? │ └─────────────┬───────────────────┘ │ YES ┌─────────────▼───────────────────┐ │ Choose Pattern Type: │ │ │ │ 📊 VARIABLE SIZE WINDOW: │ │ ✓ Two pointers (left,right)│ │ ✓ Expand right, shrink left│ │ ✓ Track condition validity │ │ │ │ 🔄 FIXED SIZE + SLIDING: │ │ ✓ HashMap for frequency │ │ ✓ Slide and update counts │ │ ✓ Check condition each step│ │ │ │ 🎯 MULTIPLE POINTERS: │ │ ✓ 3+ pointers for complex │ │ ✓ Each pointer has purpose │ │ ✓ Coordinate movements │ └─────────────────────────────────┘
Variable Size Windows
Core Pattern: Expand-Contract Strategy
Variable size sliding window adapts its length based on problem constraints. The key is knowing when to expand (move right pointer) and when to contract (move left pointer).
🌍 Real-World Applications:
📱 App Analytics: Finding time periods with at most K distinct user actions
📊 Financial Trading: Longest period with total trades ≤ budget
🌐 Network Traffic: Time windows with distinct IP addresses ≤ threshold
🎮 Gaming: Longest streak with score changes ≤ variance limit
TypeScript
// Template for Variable Size Sliding Window function variableSizeWindowTemplate<T>( arr: T[], isValidWindow: (windowData: any) => boolean, updateWindowData: (data: any, element: T, isAdding: boolean) => void ): number { let left = 0; let maxLength = 0; let windowData: any = {}; // Track window state for (let right = 0; right < arr.length; right++) { // Expand window: add arr[right] updateWindowData(windowData, arr[right], true); // Contract window if invalid while (!isValidWindow(windowData)) { updateWindowData(windowData, arr[left], false); left++; } // Update result maxLength = Math.max(maxLength, right - left + 1); } return maxLength; } // 1. Longest Substring with At Most K Distinct Characters function lengthOfLongestSubstringKDistinct(s: string, k: number): number { if (k === 0) return 0; const charCount = new Map<string, number>(); let left = 0; let maxLength = 0; for (let right = 0; right < s.length; right++) { // Expand: add character const rightChar = s[right]; charCount.set(rightChar, (charCount.get(rightChar) || 0) + 1); // Contract: remove characters to maintain at most k distinct while (charCount.size > k) { const leftChar = s[left]; const count = charCount.get(leftChar)! - 1; if (count === 0) { charCount.delete(leftChar); } else { charCount.set(leftChar, count); } left++; } maxLength = Math.max(maxLength, right - left + 1); } return maxLength; } // 2. Longest Substring Without Repeating Characters function lengthOfLongestSubstring(s: string): number { const charSet = new Set<string>(); let left = 0; let maxLength = 0; for (let right = 0; right < s.length; right++) { // Contract: remove duplicates while (charSet.has(s[right])) { charSet.delete(s[left]); left++; } // Expand: add current character charSet.add(s[right]); maxLength = Math.max(maxLength, right - left + 1); } return maxLength; } // 3. Minimum Window with Sum >= Target function minSubArrayLen(target: number, nums: number[]): number { let left = 0; let sum = 0; let minLength = Infinity; for (let right = 0; right < nums.length; right++) { sum += nums[right]; // Contract window while sum >= target while (sum >= target) { minLength = Math.min(minLength, right - left + 1); sum -= nums[left]; left++; } } return minLength === Infinity ? 0 : minLength; } // Example usage function demonstrateVariableWindow(): void { console.log("At Most K Distinct Characters:"); console.log(lengthOfLongestSubstringKDistinct("eceba", 2)); // 3 ("ece") console.log("\nLongest Substring Without Repeating:"); console.log(lengthOfLongestSubstring("abcabcbb")); // 3 ("abc") console.log("\nMinimum Window Sum:"); console.log(minSubArrayLen(7, [2,3,1,2,4,3])); // 2 }
Python
# Variable Size Sliding Window Template def variable_size_window_template(arr, is_valid_window, update_window_data): """Generic template for variable size sliding window problems""" left = 0 max_length = 0 window_data = {} # Track window state for right in range(len(arr)): # Expand window: add arr[right] update_window_data(window_data, arr[right], True) # Contract window if invalid while not is_valid_window(window_data): update_window_data(window_data, arr[left], False) left += 1 # Update result max_length = max(max_length, right - left + 1) return max_length # 1. Longest Substring with At Most K Distinct Characters def length_of_longest_substring_k_distinct(s, k): """Find longest substring with at most k distinct characters""" if k == 0: return 0 from collections import defaultdict char_count = defaultdict(int) left = 0 max_length = 0 for right, char in enumerate(s): # Expand: add character char_count[char] += 1 # Contract: maintain at most k distinct while len(char_count) > k: left_char = s[left] char_count[left_char] -= 1 if char_count[left_char] == 0: del char_count[left_char] left += 1 max_length = max(max_length, right - left + 1) return max_length # 2. Longest Substring Without Repeating Characters def length_of_longest_substring(s): """Find longest substring without repeating characters""" char_set = set() left = 0 max_length = 0 for right, char in enumerate(s): # Contract: remove duplicates while char in char_set: char_set.remove(s[left]) left += 1 # Expand: add current character char_set.add(char) max_length = max(max_length, right - left + 1) return max_length # 3. Minimum Window with Sum >= Target def min_sub_array_len(target, nums): """Find minimum length subarray with sum >= target""" left = 0 current_sum = 0 min_length = float('inf') for right in range(len(nums)): current_sum += nums[right] # Contract window while sum >= target while current_sum >= target: min_length = min(min_length, right - left + 1) current_sum -= nums[left] left += 1 return 0 if min_length == float('inf') else min_length # Example usage def demonstrate_variable_window(): print("At Most K Distinct Characters:") print(length_of_longest_substring_k_distinct("eceba", 2)) # 3 print("\nLongest Substring Without Repeating:") print(length_of_longest_substring("abcabcbb")) # 3 print("\nMinimum Window Sum:") print(min_sub_array_len(7, [2, 3, 1, 2, 4, 3])) # 2
OCaml
open Printf (* 1. Longest Substring with At Most K Distinct Characters *) let length_of_longest_substring_k_distinct s k = if k = 0 then 0 else let char_count = Hashtbl.create 26 in let left = ref 0 in let max_length = ref 0 in let n = String.length s in for right = 0 to n - 1 do let char = s.[right] in (* Expand: add character *) let current_count = try Hashtbl.find char_count char with Not_found -> 0 in Hashtbl.replace char_count char (current_count + 1); (* Contract: maintain at most k distinct *) while Hashtbl.length char_count > k do let left_char = s.[!left] in let count = Hashtbl.find char_count left_char in if count = 1 then Hashtbl.remove char_count left_char else Hashtbl.replace char_count left_char (count - 1); incr left done; max_length := max !max_length (right - !left + 1) done; !max_length (* 2. Longest Substring Without Repeating Characters *) let length_of_longest_substring s = let char_set = Hashtbl.create 26 in let left = ref 0 in let max_length = ref 0 in let n = String.length s in for right = 0 to n - 1 do let char = s.[right] in (* Contract: remove duplicates *) while Hashtbl.mem char_set char do Hashtbl.remove char_set s.[!left]; incr left done; (* Expand: add current character *) Hashtbl.add char_set char true; max_length := max !max_length (right - !left + 1) done; !max_length (* Example usage *) let demonstrate_variable_window () = printf "At Most K Distinct Characters: %d\n" (length_of_longest_substring_k_distinct "eceba" 2); printf "Longest Substring Without Repeating: %d\n" (length_of_longest_substring "abcabcbb")
Problem Type | Time Complexity | Space Complexity | Key Insight |
---|---|---|---|
Variable Size Window | O(n) | O(k) where k is distinct elements | Each element added/removed once |
Unique Characters | O(n) | O(min(m, n)) where m is charset size | Set operations for duplicates |
At Most K Constraint | O(n) | O(k) | Contract when constraint violated |
String Permutation Problems
Fixed Size Window with Frequency Matching
String permutation problems typically involve finding anagrams or character rearrangements within a string. These use fixed-size sliding windows combined with frequency counting to efficiently detect permutations.
🌍 Real-World Applications:
🔐 Cryptography: Finding anagram-based cipher patterns in encrypted text
🧬 Bioinformatics: DNA sequence analysis for gene variants and mutations
📝 Text Analysis: Plagiarism detection through character frequency matching
🎯 Search Engines: Query matching with character permutation tolerance
TypeScript
// 1. Find All Anagrams in String function findAnagrams(s: string, p: string): number[] { if (p.length > s.length) return []; const result: number[] = []; const pCount = new Map<string, number>(); const windowCount = new Map<string, number>(); // Count characters in pattern for (const char of p) { pCount.set(char, (pCount.get(char) || 0) + 1); } // Initialize window for (let i = 0; i < p.length; i++) { const char = s[i]; windowCount.set(char, (windowCount.get(char) || 0) + 1); } // Check if initial window is anagram if (mapsEqual(pCount, windowCount)) { result.push(0); } // Slide window for (let i = p.length; i < s.length; i++) { // Add new character const newChar = s[i]; windowCount.set(newChar, (windowCount.get(newChar) || 0) + 1); // Remove old character const oldChar = s[i - p.length]; const oldCount = windowCount.get(oldChar)! - 1; if (oldCount === 0) { windowCount.delete(oldChar); } else { windowCount.set(oldChar, oldCount); } // Check if current window is anagram if (mapsEqual(pCount, windowCount)) { result.push(i - p.length + 1); } } return result; } function mapsEqual(map1: Map<string, number>, map2: Map<string, number>): boolean { if (map1.size !== map2.size) return false; for (const [key, value] of map1) { if (map2.get(key) !== value) return false; } return true; } // 2. Permutation in String function checkInclusion(s1: string, s2: string): boolean { if (s1.length > s2.length) return false; const s1Count = new Map<string, number>(); const windowCount = new Map<string, number>(); // Count characters in s1 for (const char of s1) { s1Count.set(char, (s1Count.get(char) || 0) + 1); } let left = 0; let matches = 0; for (let right = 0; right < s2.length; right++) { const rightChar = s2[right]; // Expand window if (s1Count.has(rightChar)) { windowCount.set(rightChar, (windowCount.get(rightChar) || 0) + 1); if (windowCount.get(rightChar) === s1Count.get(rightChar)) { matches++; } } // Contract window if too large if (right - left + 1 > s1.length) { const leftChar = s2[left]; if (s1Count.has(leftChar)) { if (windowCount.get(leftChar) === s1Count.get(leftChar)) { matches--; } const leftCount = windowCount.get(leftChar)! - 1; if (leftCount === 0) { windowCount.delete(leftChar); } else { windowCount.set(leftChar, leftCount); } } left++; } // Check if we have a match if (matches === s1Count.size) return true; } return false; } // 3. Minimum Window Substring function minWindow(s: string, t: string): string { if (t.length > s.length) return ""; const tCount = new Map<string, number>(); for (const char of t) { tCount.set(char, (tCount.get(char) || 0) + 1); } let left = 0; let minLength = Infinity; let minStart = 0; let formed = 0; const required = tCount.size; const windowCount = new Map<string, number>(); for (let right = 0; right < s.length; right++) { const rightChar = s[right]; // Expand window windowCount.set(rightChar, (windowCount.get(rightChar) || 0) + 1); if (tCount.has(rightChar) && windowCount.get(rightChar) === tCount.get(rightChar)) { formed++; } // Contract window while (formed === required && left <= right) { // Update minimum if current window is smaller if (right - left + 1 < minLength) { minLength = right - left + 1; minStart = left; } const leftChar = s[left]; windowCount.set(leftChar, windowCount.get(leftChar)! - 1); if (tCount.has(leftChar) && windowCount.get(leftChar)! < tCount.get(leftChar)!) { formed--; } left++; } } return minLength === Infinity ? "" : s.substring(minStart, minStart + minLength); } // Example usage function demonstrateStringPermutations(): void { console.log("Find Anagrams:"); console.log(findAnagrams("cbaebabacd", "abc")); // [1, 6] console.log("\nPermutation in String:"); console.log(checkInclusion("ab", "eidbaooo")); // true console.log("\nMinimum Window Substring:"); console.log(minWindow("ADOBECODEBANC", "ABC")); // "BANC" }
Python
# String Permutation Problems # 1. Find All Anagrams in String def find_anagrams(s, p): """Find all start indices of p's anagrams in s""" if len(p) > len(s): return [] from collections import Counter result = [] p_count = Counter(p) window_count = Counter() # Initialize window for i in range(len(p)): window_count[s[i]] += 1 # Check if initial window is anagram if window_count == p_count: result.append(0) # Slide window for i in range(len(p), len(s)): # Add new character window_count[s[i]] += 1 # Remove old character old_char = s[i - len(p)] window_count[old_char] -= 1 if window_count[old_char] == 0: del window_count[old_char] # Check if current window is anagram if window_count == p_count: result.append(i - len(p) + 1) return result # 2. Permutation in String def check_inclusion(s1, s2): """Check if s2 contains permutation of s1""" if len(s1) > len(s2): return False from collections import Counter s1_count = Counter(s1) window_count = Counter() left = 0 matches = 0 for right in range(len(s2)): right_char = s2[right] # Expand window if right_char in s1_count: window_count[right_char] += 1 if window_count[right_char] == s1_count[right_char]: matches += 1 # Contract window if too large if right - left + 1 > len(s1): left_char = s2[left] if left_char in s1_count: if window_count[left_char] == s1_count[left_char]: matches -= 1 window_count[left_char] -= 1 if window_count[left_char] == 0: del window_count[left_char] left += 1 # Check if we have a match if matches == len(s1_count): return True return False # 3. Minimum Window Substring def min_window(s, t): """Find minimum window substring containing all characters of t""" if len(t) > len(s): return "" from collections import Counter, defaultdict t_count = Counter(t) window_count = defaultdict(int) left = 0 min_length = float('inf') min_start = 0 formed = 0 required = len(t_count) for right in range(len(s)): right_char = s[right] # Expand window window_count[right_char] += 1 if right_char in t_count and window_count[right_char] == t_count[right_char]: formed += 1 # Contract window while formed == required and left <= right: # Update minimum if current window is smaller if right - left + 1 < min_length: min_length = right - left + 1 min_start = left left_char = s[left] window_count[left_char] -= 1 if left_char in t_count and window_count[left_char] < t_count[left_char]: formed -= 1 left += 1 return "" if min_length == float('inf') else s[min_start:min_start + min_length] # Example usage def demonstrate_string_permutations(): print("Find Anagrams:") print(find_anagrams("cbaebabacd", "abc")) # [1, 6] print("\nPermutation in String:") print(check_inclusion("ab", "eidbaooo")) # True print("\nMinimum Window Substring:") print(min_window("ADOBECODEBANC", "ABC")) # "BANC"
OCaml
open Printf (* String Permutation Problems *) (* 1. Find All Anagrams in String *) let find_anagrams s p = let s_len = String.length s in let p_len = String.length p in if p_len > s_len then [] else let p_count = Hashtbl.create 26 in let window_count = Hashtbl.create 26 in let result = ref [] in (* Count characters in pattern *) for i = 0 to p_len - 1 do let char = p.[i] in let count = try Hashtbl.find p_count char with Not_found -> 0 in Hashtbl.replace p_count char (count + 1) done; (* Initialize window *) for i = 0 to p_len - 1 do let char = s.[i] in let count = try Hashtbl.find window_count char with Not_found -> 0 in Hashtbl.replace window_count char (count + 1) done; (* Check if initial window is anagram *) let maps_equal map1 map2 = let keys1 = Hashtbl.fold (fun k _ acc -> k :: acc) map1 [] in let keys2 = Hashtbl.fold (fun k _ acc -> k :: acc) map2 [] in List.length keys1 = List.length keys2 && List.for_all (fun k -> try Hashtbl.find map1 k = Hashtbl.find map2 k with Not_found -> false ) keys1 in if maps_equal p_count window_count then result := 0 :: !result; (* Slide window *) for i = p_len to s_len - 1 do (* Add new character *) let new_char = s.[i] in let new_count = try Hashtbl.find window_count new_char with Not_found -> 0 in Hashtbl.replace window_count new_char (new_count + 1); (* Remove old character *) let old_char = s.[i - p_len] in let old_count = Hashtbl.find window_count old_char in if old_count = 1 then Hashtbl.remove window_count old_char else Hashtbl.replace window_count old_char (old_count - 1); (* Check if current window is anagram *) if maps_equal p_count window_count then result := (i - p_len + 1) :: !result done; List.rev !result (* 2. Permutation in String *) let check_inclusion s1 s2 = let s1_len = String.length s1 in let s2_len = String.length s2 in if s1_len > s2_len then false else let s1_count = Hashtbl.create 26 in let window_count = Hashtbl.create 26 in let left = ref 0 in let matches = ref 0 in (* Count characters in s1 *) for i = 0 to s1_len - 1 do let char = s1.[i] in let count = try Hashtbl.find s1_count char with Not_found -> 0 in Hashtbl.replace s1_count char (count + 1) done; let required = Hashtbl.length s1_count in let found = ref false in for right = 0 to s2_len - 1 do if not !found then begin let right_char = s2.[right] in (* Expand window *) if Hashtbl.mem s1_count right_char then begin let count = try Hashtbl.find window_count right_char with Not_found -> 0 in Hashtbl.replace window_count right_char (count + 1); if Hashtbl.find window_count right_char = Hashtbl.find s1_count right_char then incr matches end; (* Contract window if too large *) if right - !left + 1 > s1_len then begin let left_char = s2.[!left] in if Hashtbl.mem s1_count left_char then begin if Hashtbl.find window_count left_char = Hashtbl.find s1_count left_char then decr matches; let count = Hashtbl.find window_count left_char in if count = 1 then Hashtbl.remove window_count left_char else Hashtbl.replace window_count left_char (count - 1) end; incr left end; (* Check if we have a match *) if !matches = required then found := true end done; !found (* Example usage *) let demonstrate_string_permutations () = printf "Find Anagrams: "; let anagrams = find_anagrams "cbaebabacd" "abc" in List.iter (printf "%d ") anagrams; print_newline (); printf "Permutation in String: %b\n" (check_inclusion "ab" "eidbaooo")
Problem Type | Time Complexity | Space Complexity | Key Insight |
---|---|---|---|
Find Anagrams | O(n) | O(k) where k is pattern length | Fixed window size with frequency tracking |
Permutation Check | O(n) | O(k) | Matches counter optimization |
Minimum Window | O(n + m) | O(m) where m is target length | Variable window with condition tracking |
🎯 Interview Tips & Common Patterns
Interview Strategy Guide
🔍 Pattern Recognition in Interviews:
- Keywords to Watch: "subarray", "substring", "contiguous", "window", "at most", "exactly", "permutation"
- Question Clarification: Ask about character sets, case sensitivity, empty inputs
- Edge Cases: Empty strings, single character, pattern longer than string
- Optimization Path: Start with brute force, then optimize to sliding window
💡 Implementation Tips:
- Use Maps for Frequency: HashMap/Counter for character counting
- Two Pointer Technique: left and right pointers, expand right first
- Condition Checking: Maintain valid window state efficiently
- Result Tracking: Update result when window is valid
⚠️ Common Mistakes to Avoid:
- Off-by-one Errors: Carefully handle window boundaries
- Map Operations: Check if key exists before accessing
- Window Size: Don't forget to handle fixed vs variable size
- State Management: Keep window state consistent during expansion/contraction
🌟 Interview Success Framework:
📋 Step 1 - Understand: Clarify problem constraints and requirements
🎯 Step 2 - Identify: Recognize sliding window pattern from keywords
⚙️ Step 3 - Design: Choose variable/fixed size and data structures
💻 Step 4 - Implement: Code with proper edge case handling
🧪 Step 5 - Test: Verify with examples and edge cases