Create frequency arrays 't_count' and 'window_count' initialized to zero. Count frequencies of characters in t ('ABC') and store in 't_count'.
💡 This sets up character requirements for quick checks during window expansion.
Line:t_count = [0] * 128
window_count = [0] * 128
for c in t:
t_count[ord(c)] += 1
💡 Frequency arrays allow O(1) checks for character counts instead of hash maps.
setup
Initialize variables and pointers
Initialize 'have' and 'need' to track satisfied and required characters. Set 'res' to [-1, -1] and 'res_len' to infinity. Set 'left' pointer to 0.
💡 These variables track progress and store the best window found.
Line:have, need = 0, sum(1 for x in t_count if x > 0)
res, res_len = [-1, -1], float('inf')
left = 0
💡 'need' counts unique required characters; 'have' counts how many are currently met.
expand
Expand window: move right to 0, include 'A'
Move 'right' pointer to index 0 and include 'A'. Increment 'window_count' for 'A'. Since 'A' is required and now fully satisfied, increment 'have'.
💡 Expanding window adds characters and updates counts to check requirements.
Line:for right in range(len(s)):
c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 When count of a required character matches needed count, 'have' increases.
expand
Expand window: move right to 1, include 'D'
Move 'right' to index 1 and include 'D'. 'D' is not required, so 'have' remains unchanged. Update 'window_count' for 'D'.
💡 Including non-required characters expands the window without affecting 'have'.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Only required characters affect 'have'.
expand
Expand window: move right to 2, include 'O'
Move 'right' to index 2 and include 'O'. 'O' is not required, so 'have' remains 1. Update 'window_count' for 'O'.
💡 Window expands with non-required characters without changing 'have'.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Non-required characters do not affect 'have'.
expand
Expand window: move right to 3, include 'B'
Move 'right' to index 3 and include 'B'. 'B' is required and now fully satisfied, so increment 'have' to 2.
💡 Adding a required character that meets needed count increases 'have'.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Tracking 'have' helps know when window contains all required characters.
expand
Expand window: move right to 4, include 'E'
Move 'right' to index 4 and include 'E'. 'E' is not required, so 'have' remains 2. Update 'window_count' for 'E'.
💡 Window expands with non-required characters while searching for missing required ones.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Non-required characters do not affect 'have'.
expand
Expand window: move right to 5, include 'C'
Move 'right' to index 5 and include 'C'. 'C' is required and now fully satisfied, so increment 'have' to 3, matching 'need'.
💡 Window now contains all required characters at least once.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 When 'have' equals 'need', the current window is valid and can be shrunk.
shrink
Shrink window: move left to 1, update counts
Window is valid. Current window length (6) is smaller than 'res_len' (inf), so update 'res' to [0,5] and 'res_len' to 6. Decrement count of 'A' at 'left' and move 'left' to 1. Since 'A' count in window is now less than required, decrement 'have' to 2.
💡 Shrinking window removes characters to find smaller valid substrings.
Line:while have == need:
if (right - left + 1) < res_len:
res = [left, right]
res_len = right - left + 1
window_count[ord(s[left])] -= 1
if t_count[ord(s[left])] > 0 and window_count[ord(s[left])] < t_count[ord(s[left])]:
have -= 1
left += 1
💡 Removing a required character below needed count invalidates the window.
expand
Expand window: move right to 6, include 'O'
Move 'right' to index 6 and include 'O'. 'O' is not required, so 'have' remains 2. Update 'window_count' for 'O'.
💡 Expanding window to regain all required characters after shrinking.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Non-required characters do not affect 'have'.
expand
Expand window: move right to 7, include 'D'
Move 'right' to index 7 and include 'D'. 'D' is not required, so 'have' remains 2. Update 'window_count' for 'D'.
💡 Window continues expanding to find missing required characters.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Non-required characters do not affect 'have'.
expand
Expand window: move right to 8, include 'E'
Move 'right' to index 8 and include 'E'. 'E' is not required, so 'have' remains 2. Update 'window_count' for 'E'.
💡 Window expands with non-required characters while searching for missing required ones.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Non-required characters do not affect 'have'.
expand
Expand window: move right to 9, include 'B'
Move 'right' to index 9 and include 'B'. 'B' is required and window_count for 'B' increases but was already satisfied, so 'have' remains 2.
💡 Including extra occurrences of required characters does not increase 'have' beyond 'need'.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Only the first time a required character count matches needed count increments 'have'.
expand
Expand window: move right to 10, include 'A'
Move 'right' to index 10 and include 'A'. 'A' count in window increases and matches required count again, so 'have' increments to 3, matching 'need'.
💡 Window now again contains all required characters, allowing shrinking.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Window validity is regained after expanding.
shrink
Shrink window: move left to 2, update counts
Window is valid. Current window length (10) is not smaller than 'res_len' (6). Decrement count of 'D' at index 1 (not required), so 'have' stays 3. Move 'left' to 2.
💡 Continue shrinking to find smaller valid windows.
Line:while have == need:
if (right - left + 1) < res_len:
res = [left, right]
res_len = right - left + 1
window_count[ord(s[left])] -= 1
if t_count[ord(s[left])] > 0 and window_count[ord(s[left])] < t_count[ord(s[left])]:
have -= 1
left += 1
Window still valid. Current window length (9) is not smaller than 'res_len' (6). Decrement count of 'O' at index 2 (not required), so 'have' stays 3. Move 'left' to 3.
💡 Continue shrinking to find smaller valid windows.
Line:while have == need:
if (right - left + 1) < res_len:
res = [left, right]
res_len = right - left + 1
window_count[ord(s[left])] -= 1
if t_count[ord(s[left])] > 0 and window_count[ord(s[left])] < t_count[ord(s[left])]:
have -= 1
left += 1
Window still valid. Current window length (8) is not smaller than 'res_len' (6). Decrement count of 'B' at index 3 (required). Since window_count for 'B' becomes less than required, decrement 'have' to 2. Move 'left' to 4.
💡 Removing a required character below needed count invalidates the window.
Line:while have == need:
if (right - left + 1) < res_len:
res = [left, right]
res_len = right - left + 1
window_count[ord(s[left])] -= 1
if t_count[ord(s[left])] > 0 and window_count[ord(s[left])] < t_count[ord(s[left])]:
have -= 1
left += 1
💡 Window validity depends on all required characters meeting counts.
expand
Expand window: move right to 11, include 'N'
Move 'right' to index 11 and include 'N'. 'N' is not required, so 'have' remains 2. Update 'window_count' for 'N'.
💡 Expanding window to regain missing required characters.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Non-required characters do not affect 'have'.
expand
Expand window: move right to 12, include 'C'
Move 'right' to index 12 and include 'C'. 'C' count in window increases and matches required count again, so increment 'have' to 3, matching 'need'.
💡 Window again contains all required characters, allowing shrinking.
Line:c = s[right]
window_count[ord(c)] += 1
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]:
have += 1
💡 Window validity regained after expansion.
shrink
Shrink window to find minimum: move left to 9
Shrink window repeatedly while valid. Update 'res' to [9,12] with length 4, smaller than previous 6. Move 'left' from 4 to 9, decrementing counts for 'E', 'C', 'O', 'D', and 'E'. When removing 'B' at index 9, decrement 'have' to 2, invalidating window and stopping shrinking.
💡 Shrinking finds the smallest valid window 'BANC' from indices 9 to 12.
Line:while have == need:
if (right - left + 1) < res_len:
res = [left, right]
res_len = right - left + 1
window_count[ord(s[left])] -= 1
if t_count[ord(s[left])] > 0 and window_count[ord(s[left])] < t_count[ord(s[left])]:
have -= 1
left += 1
💡 Minimum window substring is found by shrinking the valid window as much as possible.
reconstruct
Return the minimum window substring
After iteration, extract substring from 'res' indices [9,12], which is 'BANC'. This is the shortest substring containing all characters from 'ABC'.
💡 Returning the final answer completes the algorithm.
Line:left, right = res
return s[left:right+1] if res_len != float('inf') else ""
💡 Algorithm efficiently finds minimum window substring without checking all substrings.
def min_window_optimized(s: str, t: str) -> str:
if not s or not t:
return "" # STEP 1
t_count = [0] * 128 # STEP 1
window_count = [0] * 128 # STEP 1
for c in t: # STEP 1
t_count[ord(c)] += 1
have, need = 0, sum(1 for x in t_count if x > 0) # STEP 2
res, res_len = [-1, -1], float('inf') # STEP 2
left = 0 # STEP 2
for right in range(len(s)): # STEP 3-20
c = s[right] # STEP 3-20
window_count[ord(c)] += 1 # STEP 3-20
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]: # STEP 3-20
have += 1
while have == need: # STEP 9,15,16,17,20
if (right - left + 1) < res_len: # STEP 9,15,16,17,20
res = [left, right]
res_len = right - left + 1
window_count[ord(s[left])] -= 1
if t_count[ord(s[left])] > 0 and window_count[ord(s[left])] < t_count[ord(s[left])]:
have -= 1
left += 1
left, right = res # STEP 21
return s[left:right+1] if res_len != float('inf') else "" # STEP 21
📊
Minimum Window Substring - Watch the Algorithm Execute, Step by Step
Watching each pointer move and frequency update helps you understand how the algorithm efficiently finds the minimum window without checking every substring.
✓ The sliding window expands and shrinks dynamically to efficiently find the minimum substring containing all required characters.
This dynamic adjustment is hard to grasp from code alone but becomes clear when watching pointer movements and frequency updates.
✓ Tracking 'have' and 'need' counts allows quick checks of window validity without scanning the entire window each time.
Understanding this optimization clarifies why the algorithm runs in linear time.
✓ Shrinking the window as soon as all requirements are met ensures the minimum window is found, not just any valid window.
Seeing when and why the left pointer moves helps understand the minimality guarantee.
Practice
(1/5)
1. Consider the following Python code implementing the optimal sliding window approach for the character replacement problem. What is the returned value when the input is s = "AABA" and k = 1?
easy
A. 4
B. 3
C. 2
D. 1
Solution
Step 1: Trace window expansion and frequency updates
Index 0: freq[A]=1, max_count=1, window=[0,0], max_len=1
Index 1: freq[A]=2, max_count=2, window=[0,1], max_len=2
Index 2: freq[B]=1, max_count=2, window=[0,2], window size=3, replacements needed=1 ≤ k=1, max_len=3
Index 3: freq[A]=3, max_count=3, window=[0,3], window size=4, replacements needed=1 ≤ k=1, max_len=4
Step 2: Check while condition and final max_len
At index 3, window size 4 with max_count 3 means 1 replacement needed, which is allowed. So max_len updates to 4. The function returns 4.
Final Answer:
Option A -> Option A
Quick Check:
Window expands fully with one replacement allowed [OK]
Hint: Max length includes full string if replacements ≤ k [OK]
Common Mistakes:
Off-by-one error in window size calculation
Not updating max_count correctly
Misunderstanding when to shrink window
2. Consider the following Python function implementing the optimal sliding window approach to find the minimum length of a subarray containing duplicate cards. What is the returned value when calling minimumCardPickup([1, 2, 3, 2])?
def minimumCardPickup(cards):
last_seen = {}
left = 0
min_len = float('inf')
for right, card in enumerate(cards):
if card in last_seen and last_seen[card] >= left:
left = last_seen[card] + 1
last_seen[card] = right
if right - left + 1 > 1:
min_len = min(min_len, right - left + 1)
return min_len if min_len != float('inf') else -1
easy
A. 2
B. 4
C. -1
D. 3
Solution
Step 1: Trace iterations for input [1, 2, 3, 2]
Indices: 0->1, 1->2, 2->3, 3->2. At right=3, card=2 is duplicate with last_seen[2]=1 ≥ left=0, so left moves to 2.
Step 2: Calculate min_len updates
Subarray lengths considered: from indices 1 to 3 is length 3, but after left update, window is from 2 to 3 length 2, min_len updated to 2.
Final Answer:
Option A -> Option A
Quick Check:
Minimum subarray with duplicate '2' is [3,2] length 2 [OK]
Hint: Update left pointer on duplicate to shrink window [OK]
Common Mistakes:
Off-by-one error in window length
Not updating left pointer correctly
3. Consider the following Python function that counts the number of contiguous subarrays where the product of all elements is less than k. What is the returned value when calling numSubarrayProductLessThanK([10, 5, 2, 6], 100)?
def numSubarrayProductLessThanK(nums, k):
if k <= 1:
return 0
product = 1.0
count = 0
left = 0
for right in range(len(nums)):
product *= nums[right]
while product >= k and left <= right:
product /= nums[left]
left += 1
count += right - left + 1
return count
Hint: Count increments by window size each iteration [OK]
Common Mistakes:
Off-by-one in count increment
Not shrinking window correctly
4. The following code attempts to solve the Longest Repeating Character Replacement problem. Which line contains a subtle bug that can cause incorrect results on some inputs?
medium
A. Line incrementing left pointer inside the while loop
B. Line incrementing freq for s[right]
C. Line updating max_count inside the while loop after shrinking the window
D. Line updating max_len after the while loop
Solution
Step 1: Understand max_count usage
max_count should track the max frequency character in the current window efficiently without recomputing entire freq array.
Step 2: Identify bug in max_count update
Updating max_count by max(freq) inside the while loop is costly and can cause stale or incorrect max_count, leading to wrong window shrinking decisions.
Final Answer:
Option C -> Option C
Quick Check:
max_count must not be recomputed inside the shrinking loop [OK]
Hint: max_count updated only on window expansion, not shrinking [OK]
Common Mistakes:
Recomputing max_count on every shrink
Not updating frequency counts correctly
Shrinking window too early or late
5. Suppose the problem is modified so that the array can contain negative integers and zeros, and you want to count subarrays with exactly k odd numbers. Which modification to the sliding window approach is necessary to handle this variant correctly?
hard
A. Modify the odd number check to use absolute value: nums[right] % 2 != 0
B. No modification needed; the existing sliding window works for any integers
C. Use prefix sums of odd counts instead of sliding window to handle negatives
D. Sliding window must reset when encountering negative numbers to avoid incorrect counts
Solution
Step 1: Identify odd number condition with negatives
Negative odd numbers still have odd absolute value, so check must consider absolute value.
Step 2: Modify odd check
Change condition to nums[right] % 2 != 0 or abs(nums[right]) % 2 == 1 to correctly count odd numbers.
Final Answer:
Option A -> Option A
Quick Check:
Oddness depends on absolute value, not sign [OK]
Hint: Oddness depends on absolute value, not sign [OK]
Common Mistakes:
Assuming negative numbers are never odd
Resetting sliding window unnecessarily
Switching to prefix sums without need
Recommended
Ready to prove it?
Pass the test and this pattern moves unknownstrongwhile earning +1 Grip toward your certificate.