Sliding Window
Master this topic with zero to advance depth.
Sliding Window Mental Models
The Sliding Window technique is a powerful optimization used to convert O() or O() nested loop problems into O() linear time. It is primarily used for contiguous subarrays or substrings.
1. Fixed Size Window (Static)
The window size k is constant. We slide the window by adding one element from the right and removing one from the left.
Window Size K=3, nums = [1, 2, 3, 4, 5, 6]
Step 1: [1, 2, 3], 4, 5, 6 (Sum: 6)
Step 2: 1, [2, 3, 4], 5, 6 (Sum: 9)
Step 3: 1, 2, [3, 4, 5], 6 (Sum: 12)2. Variable Size Window (Dynamic)
The window size expands and shrinks based on a condition. We use two pointers: left and right.
Target Sum >= 7, nums = [2, 3, 1, 2, 4, 3]
[2, 3, 1], 2, 4, 3 (Sum: 6 < 7, Expand Right)
[2, 3, 1, 2], 4, 3 (Sum: 8 >= 7, Valid! Shrink Left)
2, [3, 1, 2], 4, 3 (Sum: 6 < 7, Expand Right)3. Universal Sliding Window Template (Variable)
Most variable window problems follow this structure:
def sliding_window(nums):
left = 0
state = initial_state()
res = 0
for right in range(len(nums)):
# 1. Expand: Update state with nums[right]
update_state(state, nums[right])
# 2. Shrink: While condition is violated
while condition_violated(state):
remove_from_state(state, nums[left])
left += 1
# 3. Update Result: Window is now valid
res = max(res, right - left + 1)
return res4. Common Patterns & Pitfalls
- State Maintenance: Use a
Sumvariable for numeric problems, aHashMapfor character frequency, or aDequefor window maximums. - Shrinking Condition: For "At Most K", shrink when
count > K. For "Exactly K", calculateatMost(K) - atMost(K-1). - Integer Overflow: When summing large arrays, ensure the
Sumvariable is a 64-bit integer (e.g.,longin Java/C++). - Empty String/Array: Always handle edge cases where
n < kor the input is empty.
Maximum Sum Subarray of Size K
Given an array of integers nums and a positive integer k, find the maximum sum of any contiguous subarray of size k.
Visual Representation
nums = [2, 1, 5, 1, 3, 2], k = 3
Step 1: [2, 1, 5], 1, 3, 2 (Sum = 8)
Step 2: 2, [1, 5, 1], 3, 2 (Sum = 7)
Step 3: 2, 1, [5, 1, 3], 2 (Sum = 9) -> MAX
Step 4: 2, 1, 5, [1, 3, 2] (Sum = 6)Examples
Subarray [5, 1, 3] has the maximum sum of 9.
Level I: Brute Force (Nested Loops)
Intuition
The most basic way to solve this is to check every possible contiguous subarray of size k and calculate its sum. We keep track of the maximum sum found so far.
Thought Process
- Iterate through every possible starting index
ifrom0ton - k. - For each
i, start another loop to sum up the nextkelements. - Update
maxSumif the current subarray sum is larger.
Pattern Identification
This is a Naive Search pattern. We are re-calculating the sum for overlapping parts of subarrays, which is the main bottleneck.
Detailed Dry Run
| Outer Loop (i) | Inner Loop (j) | Current Sum | Max Sum | Action |
|---|---|---|---|---|
| 0 | 0, 1, 2 | 2+1+5 = 8 | 8 | Init Max |
| 1 | 1, 2, 3 | 1+5+1 = 7 | 8 | No Change |
| 2 | 2, 3, 4 | 5+1+3 = 9 | 9 | Update Max |
| 3 | 3, 4, 5 | 1+3+2 = 6 | 9 | No Change |
Level II: Prefix Sums (O(N) Space)
Intuition
We can precompute a prefix sum array where prefix[i] stores the sum of elements from 0 to i-1. The sum of any subarray nums[i...j] can then be calculated in as prefix[j+1] - prefix[i].
Thought Process
- Create an array
prefixof sizen + 1. - Fill
prefixsuch thatprefix[i+1] = prefix[i] + nums[i]. - Iterate from
i = 0ton - kand calculatesum = prefix[i+k] - prefix[i]. - Update
maxSumaccordingly.
Pattern: Space-Time Tradeoff
This approach reduces the calculation time to per subarray but uses extra space.
Level III: Optimal (Sliding Window)
Intuition
Thought Process
We can observe that two consecutive subarrays of size k share k-1 elements. Instead of re-summing everything, we can 'slide' the window: add the next element (Right) and subtract the element that is no longer in the window (Left).
Pattern: Sliding Window (Fixed Size)
- Expand: Add
nums[right]towindowSum. - Shrink/Slide: When
right >= k - 1, the window is full. Update the result, then subtractnums[left]and incrementleftto prepare for the next step.
Mandatory Variables
left: Pointer to the start of the window.windowSum: Current sum of thekelements.maxSum: Best sum found so far.
Detailed Dry Run
| Step | Right | Left | windowSum | maxSum | Action |
|---|---|---|---|---|---|
| 1 | 0 | 0 | 2 | 0 | Expand |
| 2 | 1 | 0 | 3 | 0 | Expand |
| 3 | 2 | 0 | 8 | 8 | Window Full, Update Max |
| 4 | 3 | 1 | 7 | 8 | Slide (Sub 2, Add 1) |
| 5 | 4 | 2 | 9 | 9 | Slide (Sub 1, Add 3), Update Max |
Minimum Size Subarray Sum
Given an array of positive integers nums and a positive integer target, return the minimal length of a contiguous subarray of which the sum is greater than or equal to target. If there is no such subarray, return 0 instead.
Visual Representation
target = 7, nums = [2, 3, 1, 2, 4, 3]
[2, 3, 1, 2], 4, 3 (Sum: 8 >= 7, Len: 4)
2, [3, 1, 2, 4], 3 (Sum: 10 >= 7, Len: 4)
2, 3, 1, [2, 4, 3] (Sum: 9 >= 7, Len: 3) -> MINIMALExamples
The subarray [4,3] has the minimal length under the problem constraint.
Level I: Brute Force (O(N^2))
Intuition
Check all possible subarrays and find the one with sum >= target and minimum length.
Thought Process
- Use two nested loops to define every subarray
nums[i...j]. - Calculate the sum of each subarray.
- If
sum >= target, updateminLen = min(minLen, j - i + 1).
Pattern: Subarray Enumeration
Level III: Optimal (Variable Sliding Window)
Intuition
Categorization
This is the classic Variable Size Window problem. We expand the window until the sum meets the target, then shrink from the left to find the smallest valid window.
Thought Process
- Maintain a
windowSumand aleftpointer. - Iterate
rightfrom 0 ton-1(Expand). - Add
nums[right]towindowSum. - While
windowSum >= target:- Update
minLenwithright - left + 1. - Subtract
nums[left]fromwindowSumand incrementleft(Shrink).
- Update
Mandatory Variables
left: Pointer to the start of the window.windowSum: Current sum of the window content.minLen: Smallest valid window length found.
Detailed Dry Run
| Step | L | R | windowSum | minLen | Action |
|---|---|---|---|---|---|
| 1 | 0 | 0 | 2 | inf | Expand |
| 2 | 0 | 1 | 5 | inf | Expand |
| 3 | 0 | 2 | 6 | inf | Expand |
| 4 | 0 | 3 | 8 | 4 | Sum >= 7! Update minLen, Shrink L |
| 5 | 1 | 3 | 6 | 4 | Sum < 7. Expand R |
Longest Substring Without Repeating Characters
Given a string s, find the length of the longest substring without repeating characters.
Visual Representation
s = "abcabcbb"
L R
| |
a b c a b c b b
[---] Set: {a, b, c}, Len: 3
L R
| |
a b c a b c b b
[---] Set: {b, c, a}, Len: 3Examples
The answer is "abc", with the length of 3.
Level I: Brute Force
Intuition
Check every possible substring and verify if it contains unique characters. A string of length has substrings. Checking each takes , leading to . We can optimize the check using a Set to .
Thought Process
- Use two nested loops to define every potential substring
s[i...j]. - For each substring, use a third loop or a Set to check for duplicates.
- If no duplicates, update the
maxLength.
Detailed Dry Run
| i | j | Substring | Unique? | Max Len |
|---|---|---|---|---|
| 0 | 0 | "a" | Yes | 1 |
| 0 | 1 | "ab" | Yes | 2 |
| 0 | 2 | "abc" | Yes | 3 |
| 0 | 3 | "abca" | No | 3 |
| 1 | 3 | "bca" | Yes | 3 |
Level II: Binary Search on Answer (O(N log N))
Intuition
We can binary search for the maximum length k. For a fixed k, we check if there exists any substring of length k with no repeating characters using a fixed-size sliding window. If a valid substring of length k exists, then any length smaller than k is also possible, so we search in [k, right]. Otherwise, we search in [left, k-1].
Thought Process
- Search range for maximum length is
[1, n]. - In each step of binary search (length
mid):- Use a sliding window of size
midto check for uniqueness. - If unique substring found,
ans = mid,low = mid + 1. - Else,
high = mid - 1.
- Use a sliding window of size
Pattern: Search on Answer Space
Level III: Optimal (Sliding Window)
Intuition
Categorization
This is a Variable Size Window problem because the length of the unique substring changes as we encounter duplicates.
Thought Process
Instead of checking all substrings, we maintain a sliding window [left, right]. As we expand right, we add the character to a Set. If s[right] is already in the Set, we have a violation. We must shrink the window from the left until the duplicate is gone.
Mandatory Variables
left: Marks the start of the current unique substring.charSet: Stores characters in the current window for duplicate checking.maxLength: Tracks the largest window size found.
Detailed Dry Run
| Step | L | R | Char | Action | Set Content | Max Len |
|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 'a' | Add 'a' | {a} | 1 |
| 2 | 0 | 1 | 'b' | Add 'b' | {a, b} | 2 |
| 3 | 0 | 2 | 'c' | Add 'c' | {a, b, c} | 3 |
| 4 | 0 | 3 | 'a' | Dupe 'a'! Shrink L | {b, c, a} | 3 |
| 5 | 1 | 4 | 'b' | Dupe 'b'! Shrink L | {c, a, b} | 3 |
⚠️ Common Pitfalls & Tips
Be careful with the window shrink condition. It must be a while loop to remove all characters until the duplicate is gone. Also, remember to add the current character after the shrink loop.
Permutation in String
Given two strings s1 and s2, return true if s2 contains a permutation of s1, or false otherwise.
Visual Representation
s1 = "ab", s2 = "eidbaooo"
Target Count: {a:1, b:1}
[e, i] d b a o o o Count: {e:1, i:1} (Mismatch)
e [i, d] b a o o o Count: {i:1, d:1} (Mismatch)
e i [d, b] a o o o Count: {d:1, b:1} (Mismatch)
e i d [b, a] o o o Count: {b:1, a:1} (Matches!)Examples
s2 contains one permutation of s1 ("ba").
Level I: Brute Force
Intuition
To check if s2 contains a permutation of s1, we can generate all substrings of s2 that have the same length as s1, and then check if that substring is a permutation of s1 (by sorting both and comparing).
Thought Process
- Iterate through all substrings of
s2with length equal tos1.length(). - For each substring, sort its characters and also sort the characters of
s1. - If the sorted strings match, then a permutation exists.
Detailed Dry Run
| Window in s2 | Sorted Window | Sorted s1 (ab) | Match? |
|---|---|---|---|
| "ei" | "ei" | "ab" | No |
| "id" | "di" | "ab" | No |
| "db" | "bd" | "ab" | No |
| "ba" | "ab" | "ab" | YES |
Level II: Frequency Counter (O(N * 26))
Intuition
Instead of sorting, we can compare character frequency maps. This is still a 'sliding' concept but we re-build or re-check the entire map of size 26 for each window. Since the alphabet size is constant (26), this is technically O(N * 26).
Thought Process
- Count frequencies of characters in
s1. - Iterate through
s2and for every window of sizes1.length(), build a new frequency map. - Compare the window's map with
s1'smap.
Pattern: Frequency Hashing
Level III: Optimal (Sliding Window)
Intuition
Categorization
This is a Fixed Size Window problem because any permutation of s1 must have exactly the same length as s1.
Thought Process
Instead of sorting every substring, we use character frequency counts. A permutation of s1 will have the exact same character frequency count as s1. We maintain a window of size s1.length() in s2 and update the counts as we slide.
Mandatory Variables
left: Marks the start of the window.s1Counts: Frequency map of characters ins1.windowCounts: Frequency map of characters in the currents2window.
Detailed Dry Run
| Window | s2 Fragment | Counts Match? | Action |
|---|---|---|---|
| [0, 1] | "ei" | No | Expand R, Shrink L |
| [1, 2] | "id" | No | Expand R, Shrink L |
| [2, 3] | "db" | No | Expand R, Shrink L |
| [3, 4] | "ba" | YES! | Return True |
⚠️ Common Pitfalls & Tips
Be careful to check the final window after the loop ends, as the last comparison might not happen inside the loop depending on how you structure it.
Minimum Window Substring
Given two strings s and t, return the minimum window substring of s such that every character in t (including duplicates) is included in the window.
Visual Representation
s = "ADOBECODEBANC", t = "ABC"
Window Expand (Found all ABC):
[ADOBEC]ODEBANC (Len: 6)
Window Shrink (Still have ABC):
A [DOBEC]ODEBANC (Len: 5) -> [DOBEC] (Wait, missing A!)
Final Optimal Window:
ADOBECODE [BANC] (Len: 4)Examples
Level I: Brute Force
Intuition
Check every possible substring of s and verify if it contains all characters of t in the required frequencies. Keep track of the minimum length substring that satisfies this.
Thought Process
- Iterate through all possible
startandendindices to define every substring. - For each substring, count its character frequencies.
- Compare these frequencies with the target frequency map of
t. - If the substring is valid, update the
minWindowresult.
Detailed Dry Run
| Substring | Valid? | Min Length | Min Window |
|---|---|---|---|
| "ADOBE" | No (missing C) | inf | "" |
| "ADOBEC" | YES! | 6 | "ADOBEC" |
| "BANC" | YES! | 4 | "BANC" |
Level II: Binary Search on Window Size
Intuition
The shortest valid window length must be between len(t) and len(s). We can binary search for this length k. For each k, we use a fixed-size sliding window of length k to check if any valid substring exists. If it does, we try smaller lengths; otherwise, we try larger ones.
Detailed Dry Run
Input: s = "ADOBECODEBANC", t = "ABC"
- Binary Search range:
[3, 13]. - Try
k = 8: SubstringADOBECODis valid. Search[3, 7]. - Try
k = 5: SubstringEBANCis valid. Search[3, 4]. - Try
k = 4: SubstringBANCis valid. Search[3, 3]. - Try
k = 3: No window of size 3 is valid. - Result:
k = 4, returnBANC.
Level III: Optimal (Sliding Window)
Intuition
Categorization
This is a Variable Size Window problem. We need to find the smallest range [L, R] that satisfies the requirement.
Thought Process
- Use a map
targetCountsto store required characters int. - Expand
rightuntil the window contains all characters int. We use ahavecounter vs aneedcounter for validity check. - Once valid, shrink
leftas much as possible while maintaining validity to find the smallest window. - Update the
minWindowresult whenever a smaller valid window is found.
Mandatory Variables
left,right: Window pointers.targetMap: Frequencies of characters int.windowMap: Frequencies of characters in the current window.have,need: Variables to track progress toward the target frequency.
Detailed Dry Run
s = "ADOBEC", t = "ABC"
| Step | L | R | Char | Window Map | Have/Need | Action |
|---|---|---|---|---|---|---|
| 1 | 0 | 0 | 'A' | {A:1} | 1/3 | Expand |
| 2-4 | 0 | 4 | 'E' | {A:1, B:1} | 2/3 | Expand |
| 5 | 0 | 5 | 'C' | {A:1, B:1, C:1} | 3/3 | VALID! |
| 6 | 1 | 5 | 'D' | {B:1, C:1} | 2/3 | Shrink (Invalidated) |
⚠️ Common Pitfalls & Tips
Be careful with character counts if t has duplicates. Using a have/need counter for unique characters is more robust than a total character count.
Max Consecutive Ones III
Given a binary array nums and an integer k, return the maximum number of consecutive 1's in the array if you can flip at most k 0's.
Visual Representation
nums = [1,1,1,0,0,0,1,1,1,1,0], k = 2
L R
| |
1 1 1 0 0 0 1 1 1 1 0
[-----] (Zeros: 1 <= 2, Expand R)
L R
| |
1 1 1 0 0 0 1 1 1 1 0
[-------] (Zeros: 2 <= 2, Expand R)
L R
| |
1 1 1 0 0 0 1 1 1 1 0
[---------] (Zeros: 3 > 2! Shrink L)Examples
Level I: Brute Force
Intuition
Check every possible subarray and count the number of zeros in it. If the zero count is less than or equal to k, the subarray is valid. Keep track of the maximum length of such valid subarrays.
Thought Process
- Use two nested loops to define all possible subarrays
[i, j]. - In the inner loop, count zeros.
- If
zeros <= k, updatemaxLen = max(maxLen, j - i + 1). - If
zeros > k, break the inner loop (optimization).
Detailed Dry Run
| i | j | nums[j] | Zeros | Max Len | Action |
|---|---|---|---|---|---|
| 0 | 0 | 1 | 0 | 1 | Update |
| 0 | 3 | 0 | 1 | 4 | Update |
| 0 | 4 | 0 | 2 | 5 | Update |
| 0 | 5 | 0 | 3 | 5 | Break (Zeros > k) |
Level II: Binary Search on Answer (O(N log N))
Intuition
We can binary search for the maximum possible length L of a valid subarray. For a fixed length L, we can check if there exists any subarray of length L with at most k zeros using a fixed-size sliding window in time.
Thought Process
- Search range for length is
[0, n]. - For a fixed
midlength:- Slide a window of size
midacross the array. - Count zeros in the window.
- If any window has
zeros <= k, thenmidis possible.
- Slide a window of size
- If possible, search higher; else search lower.
Pattern: Search on Answer Space
Level III: Optimal (Sliding Window)
Intuition
Thought Process
Instead of checking every subarray, we use a window [left, right]. We expand the window by moving right. If the number of zeros in the window exceeds k, we must shrink the window from the left until the zero count is back to k.
Pattern: Variable Size Sliding Window
- Expand:
windowSum += (nums[right] == 0 ? 1 : 0) - Constraint:
zeros > k - Shrink:
while (zeros > k) { if (nums[left] == 0) zeros--; left++; }
Detailed Dry Run
| R | Num | Zeros | Action | Max Len |
|---|---|---|---|---|
| 3 | 0 | 1 | Expand | 4 |
| 4 | 0 | 2 | Expand | 5 |
| 5 | 0 | 3 | Shrink L (until zeros=2) | 5 |
| 10 | 0 | 2 | Expand | 6 |
⚠️ Common Pitfalls & Tips
Be careful with the while loop condition. It should be zeros > k. Also, ensure you update maxLen after the shrink loop is done to ensure the window is valid.
Fruit Into Baskets
You are visiting a farm that has a single row of fruit trees. Each tree has a type. You have TWO baskets, each holding ONE type. Find the maximum fruits you can collect in a contiguous range.
Visual Representation
fruits = [1, 2, 1, 2, 3]
L R
| |
1, 2, 1, 2, 3 Types: {1, 2}, Count: 4
[---------]
L R
| |
1, 2, 1, 2, 3 Types: {2, 3}, Count: 2
[-]Examples
Level I: Brute Force
Intuition
Check every possible contiguous range of trees and count how many fruits and types are in that range. If the number of types is ≤ 2, the range is valid. Track the maximum fruits collected.
Thought Process
- Use two nested loops to define all possible ranges
[i, j]. - In the inner loop, add each fruit to a set to count types.
- If types ≤ 2, update
maxFruits = max(maxFruits, j - i + 1). - If types > 2, break the inner loop.
Detailed Dry Run
| i | j | fruits[j] | Types | Fruits | Max |
|---|---|---|---|---|---|
| 0 | 0 | 1 | {1} | 1 | 1 |
| 0 | 1 | 2 | {1, 2} | 2 | 2 |
| 0 | 2 | 1 | {1, 2} | 3 | 3 |
| 0 | 3 | 2 | {1, 2} | 4 | 4 |
| 0 | 4 | 3 | {1, 2, 3} | 5 | 4 (Break) |
Level II: Binary Search on Answer (O(N log N))
Intuition
We can binary search for the maximum number of fruits L we can collect. For a fixed length L, we check if any contiguous subarray of length L contains at most 2 distinct fruit types using a fixed-size sliding window.
Thought Process
- Search range is
[1, n]. - For a fixed
midlength:- Slide a window of size
mid. - Maintain a frequency map of fruit types in the window.
- If
map.size() <= 2at any point, the lengthmidis possible.
- Slide a window of size
- Adjust the search range based on possibility.
Pattern: BS on Answer + Sliding Window Check
Level III: Optimal (Sliding Window)
Intuition
Thought Process
We need to find the longest subarray that contains exactly or at most two distinct elements. We use a frequency map to track the fruits in our window. When the number of types (map size) exceeds 2, we shrink the window from the left.
Pattern: Variable Size Sliding Window (K Distinct Elements)
- Expand:
count[fruits[right]]++ - Constraint:
count.size() > 2 - Shrink:
while (count.size() > 2) { count[fruits[left]]--; if (count[fruits[left]] == 0) count.remove(fruits[left]); left++; }
Detailed Dry Run
| R | Fruit | Map | Map Size | Action | Result |
|---|---|---|---|---|---|
| 0 | 1 | {1:1} | 1 | Expand | 1 |
| 1 | 2 | {1:1, 2:1} | 2 | Expand | 2 |
| 2 | 1 | {1:2, 2:1} | 2 | Expand | 3 |
| 3 | 2 | {1:2, 2:2} | 2 | Expand | 4 |
| 4 | 3 | {1:2, 2:2, 3:1} | 3 | Shrink L (remove 1s) | 4 |
⚠️ Common Pitfalls & Tips
Be careful with shrinking: ensure you decrement the count and remove the key from the map ONLY when the count reaches zero. Forgetting to remove the key will result in an incorrect count.size().
Longest Repeating Character Replacement
You are given a string s and an integer k. You can change at most k characters to any other uppercase English character. Return the length of the longest substring containing the same letter.
Visual Representation
s = "AABABBA", k = 1
[A A B A] B B A (Len: 4, MaxFreq char 'A': 3)
Window Size 4 - MaxFreq 3 = 1 (Matches k=1, VALID)
[A A B A B] B A (Len: 5, MaxFreq char 'A': 3)
Window Size 5 - MaxFreq 3 = 2 (Exceeds k=1, INVALID)Examples
Replace the two 'A's with two 'B's or vice-versa.
Level I: Brute Force
Intuition
For every possible substring, check if we can make all characters in that substring the same by replacing at most k characters. To do this for a substring, we find the frequency of the most common character and check if (length - maxFreq) <= k.
Thought Process
- Use two nested loops to iterate through all substrings
[i, j]. - For each substring, count character frequencies.
- Find the character with the maximum frequency (
maxFreq). - If
(substringLength - maxFreq) <= k, the substring is valid. - Update
maxLenaccordingly.
Detailed Dry Run
| Substring | Freq Map | Max Freq | Len - MaxFreq | k | Valid? |
|---|---|---|---|---|---|
| "AAB" | {A:2, B:1} | 2 | 3-2 = 1 | 1 | YES |
| "AABA" | {A:3, B:1} | 3 | 4-3 = 1 | 1 | YES |
| "AABAB" | {A:3, B:2} | 3 | 5-3 = 2 | 1 | NO |
Level II: Binary Search on Answer (O(N log N * 26))
Intuition
We can binary search for the maximum possible length L of a valid substring. For a fixed length L, we use a sliding window of size L to check if at least one window satisfies (L - maxFreq) <= k. Finding maxFreq in a window takes O(26).
Thought Process
- Search range for
Lis[0, n]. - For each
midlength step in binary search:- Slide a window of size
midacross the string. - Count characters and find the most frequent character.
- If
mid - maxFreq <= k, then lengthmidis possible.
- Slide a window of size
- Adjust the search range accordingly.
Pattern: Search on Answer Space
Level III: Optimal (Sliding Window)
Intuition
Thought Process
Instead of re-calculating everything, we use a sliding window [left, right]. We maintain the frequency of the most common character within the current window. The window is valid as long as (WindowLength - MaxFrequency) <= k. This works because WindowLength - MaxFrequency represents the minimum number of characters we need to replace to make all characters in the window the same.
Pattern: Variable Size Sliding Window
- Expand: Increment frequency of
s[right]and updatemaxFreq. - Constraint:
(right - left + 1) - maxFreq > k - Shrink: Increment
leftand decrement its frequency. Note:maxFreqdoesn't strictly need to be decreased, as only a largermaxFreqcan increase our result.
Detailed Dry Run
| R | Char | Freq Map | Max Freq | Valid? | Action |
|---|---|---|---|---|---|
| 0 | A | {A:1} | 1 | Yes | Expand |
| 1 | A | {A:2} | 2 | Yes | Expand |
| 2 | B | {A:2, B:1} | 2 | Yes | Expand |
| 3 | A | {A:3, B:1} | 3 | Yes | Expand |
| 4 | B | {A:3, B:2} | 3 | No | Shrink L |
⚠️ Common Pitfalls & Tips
You don't need to decrement maxFreq when shrinking the window. While this seems counter-intuitive, think about it: we only care about the largest maxFreq we've ever seen because that's the only way to get a larger maxLen.
Find All Anagrams in a String
Given strings s and p, return an array of all the start indices of p's anagrams in s.
Visual Representation
s = "cbaebabacd", p = "abc"
[c b a] e b a b a c d (Window: {a:1, b:1, c:1}, Matches! Index 0)
c [b a e] b a b a c d (Window: {a:1, b:1, e:1}, Mismatch)
...
c b a e b a b [a c d] (Wait, missing b! Mismatch)Examples
Level I: Brute Force
Intuition
For every possible substring of s that has the same length as p, check if it's an anagram of p. To check for anagrams, we can sort both strings or compare their character frequency maps.
Thought Process
- Iterate through
swith a loop from0ton - m(wheremisp.length()). - Extract the substring of length
m. - Compare the substring with
pusing a frequency map or sorting. - If they match, add the starting index to the result list.
Detailed Dry Run
| Start Index | Substring | Is Anagram of "abc"? | Result |
|---|---|---|---|
| 0 | "cba" | YES | [0] |
| 1 | "bae" | NO | [0] |
| 6 | "abc" | YES | [0, 6] |
Level II: Optimized Brute Force (O(N * 26))
Intuition
Instead of re-comparing the whole substring or sorting, we can compare the frequency counts of characters. Since the alphabet is small (26), comparing two maps of size 26 is , which is effectively constant time.
Thought Process
- Count character frequencies for
p. - Iterate through
sand for every window of sizem, build the frequency map for that window. - Compare the map with
pCountusingArrays.equals(Java) or==(Python/Go).
Pattern: Constant-Time Map Comparison
Level III: Optimal (Sliding Window)
Intuition
Thought Process
Since an anagram must have exactly the same length as p, we use a Fixed Size Sliding Window of length m. We maintain frequency counts for both p and the current s window. Instead of re-calculating the frequency map for every window, we 'slide' it: add the incoming character and remove the outgoing one.
Pattern: Fixed Size Sliding Window
- Init: Fill frequency maps for
pand the firstmcharacters ofs. - Slide: For
ifrommton-1:- Add
s[i]tosCount. - Remove
s[i - m]fromsCount. - Compare maps.
- Add
Detailed Dry Run
| Window | Start Index | Added | Removed | Match? | | :--- | :--- | :--- | :--- | :--- | :--- | | [0, 2] | 0 | - | - | YES | | [1, 3] | 1 | 'e' | 'c' | NO | | [6, 8] | 6 | 'c' | 'b' | YES |
⚠️ Common Pitfalls & Tips
Be careful with the sliding logic: ensure you add the NEW character and remove the OLD character at the same step to keep the window size constant. Also, check the very first window before the loop starts.
Sliding Window Maximum
Given an array nums and a window size k, return the maximum in each sliding window.
Visual Representation
nums = [1, 3, -1, -3, 5, 3, 6, 7], k = 3
Window: [1, 3, -1] Max: 3
Window: [3, -1, -3] Max: 3
Window: [-1, -3, 5] Max: 5
Monotonic Deque (indices):
Step 1 (1): [0]
Step 2 (3): [1] (discarded 0 since 3 > 1)
Step 3 (-1): [1, 2]
Step 4 (-3): [1, 2, 3]
Step 5 (5): [4] (discarded 1, 2, 3 since 5 is larger)Examples
Level I: Brute Force
Intuition
For every possible sliding window of size k, iterate through all its elements to find the maximum value.
Thought Process
- There are
n - k + 1windows in an array of sizen. - For each window starting at index
i, loop fromitoi + k - 1to find the max. - Store the result in an array.
Detailed Dry Run
| Window | Elements | Max | Result |
|---|---|---|---|
| [0, 2] | [1, 3, -1] | 3 | [3] |
| [1, 3] | [3, -1, -3] | 3 | [3, 3] |
| [2, 4] | [-1, -3, 5] | 5 | [3, 3, 5] |
Level II: Heaps / Priority Queue (O(N log N))
Intuition
To find the maximum in a window efficiently, we can use a Max-Heap (Priority Queue). The heap stores pairs of (value, index). For each window, we add the current element to the heap and then remove elements from the top of the heap if their indices are outside the current window.
Thought Process
- Use a Max-Heap to store
[nums[i], i]. - In each step, add the current element to the heap.
- Peek at the top of the heap. If the index is less than
i - k + 1, it's outside the window; remove it (pop). - The top of the heap is the maximum for the current window.
Pattern: Sliding Window + Heap Optimization
Level III: Optimal (Monotonic Deque)
Intuition
Thought Process
We need a way to find the maximum in the current window in . A Monotonic Deque stores indices of elements in strictly decreasing order of their values (nums[dq.front()] is always max).
Logic Steps
- Clean Deque (Old indices): If the index at the front is outside the current window
[i-k+1, i], remove it. - Maintain Monotonicity: Before adding
nums[i], remove all indices from the back whose values are . They can never be the maximum in any future window containingnums[i]. - Add Result: Once the window reaches size
k, the value atnums[dq.front()]is the maximum for the current window.
Detailed Dry Run
| i | Val | Action | Deque (Indices) | Result |
|---|---|---|---|---|
| 1 | 3 | Pop 0 (1 < 3) | [1] | - |
| 2 | -1 | Push 2 | [1, 2] | [3] |
| 3 | -3 | Push 3 | [1, 2, 3] | [3, 3] |
| 4 | 5 | Pop all (all < 5) | [4] | [3, 3, 5] |
⚠️ Common Pitfalls & Tips
Storing values instead of indices in the deque makes it hard to check if the maximum element has slid out of the window. Always store indices.
Minimum Window Subsequence
Given strings s1 and s2, find the minimum contiguous substring of s1 such that s2 is a subsequence of that part.
Visual Representation
s1 = "abcdebdde", s2 = "bde"
Forward (Find b...d...e):
a b c d e b d d e
^ ^ ^ (Found at index 4, end=5)
Backward (Optimize Start from index 4):
a b c d e
^ ^ (Start=1, b is at idx 1)
Result: "bcde" (Len 4)Examples
Level I: Brute Force
Intuition
For every possible starting point in s1, scan forward to see if s2 can be formed as a subsequence. If it can, record the length and update the minimum window found so far.
Thought Process
- Iterate through all indices
iins1as potential starts. - From
i, use another pointerkto scans1, andjto scans2. - If
s1[k] == s2[j], incrementj. - If
j == s2.length(), we found a window[i, k]. Its length isk - i + 1. - Track the minimum such length and the starting position.
Detailed Dry Run
| Start | s1 Scan | s2 Matched | Window | Min |
|---|---|---|---|---|
| 1 (b) | bcde | b, d, e (All) | [1, 4] | 4 |
| 5 (b) | bdde | b, d, e (All) | [5, 8] | 4 |
Level II: Dynamic Programming (O(N * M))
Intuition
We can use a 2D DP table dp[i][j] that stores the starting index of the shortest substring in s1[0...i] such that s2[0...j] is a subsequence. If no such substring exists, we store -1.
Thought Process
dp[i][0]isiifs1[i] == s2[0], elsedp[i-1][0].- For
j > 0, ifs1[i] == s2[j], thendp[i][j] = dp[i-1][j-1](we extend the subsequence start index from the previous match). - Otherwise,
dp[i][j] = dp[i-1][j](we inherit the latest start index).
Pattern: Subsequence Search with DP
Level III: Optimal (Sliding Window + Dual Scan)
Intuition
Thought Process
Unlike 'Minimum Window Substring' (set of chars), this requires subsequence (specific order). We scan forward to find any window containing the subsequence. Once found, we scan backward from the current end to find the best starting point for that specific window.
Patterns
- Forward Pass: Expand to find a valid window ending at
i. - Backward Pass: From
i, scan back to find the latest valid startj. This handles cases likeS=abbbcde, T=abcdemore accurately by skipping redundantbs at the beginning of the window.
Detailed Dry Run
| i | j | Action | Result |
|---|---|---|---|
| 1 | 0 | 'b' matched | i=2, j=1 |
| 4 | 2 | 'e' matched | Window Found! |
| Backward | - | Scan back from i=4 | Start=1 |
| Min | - | Check "bcde" | MinLen=4 |
⚠️ Common Pitfalls & Tips
The backward scan is crucial for finding the optimal start. Without it, you might find a valid window but not the minimum one starting from a later occurrence of the first character.
Maximum Points You Can Obtain from Cards
You can take exactly k cards from either the beginning or the end of the row to maximize your point total.
Visual Representation
cardPoints = [1, 2, 3, 4, 5, 6, 1], k = 3
Take cards from ends (Total: 7, k: 3):
(1, 2) ... (1) Total: 4
(1) ... (6, 1) Total: 8
... ... (5, 6, 1) Total: 12 (MAX)
Trick: Minimize the sum of the remaining (n-k) cards!
Remaining window size = 7 - 3 = 4Examples
Level I: Brute Force
Intuition
Try all possible combinations of taking i cards from the left and k - i cards from the right (where 0 <= i <= k). For each combination, calculate the sum and track the maximum.
Thought Process
- Let
ibe the number of cards from the left end. - Then
k - icards must be taken from the right end. - Iterate
ifrom0tok. - Calculate
Sum = (Sum of first i) + (Sum of last k - i). - Tracking the maximum sum.
Detailed Dry Run
| Cards from Left | Cards from Right | Total Sum |
|---|---|---|
| 0 | 3 (5, 6, 1) | 12 |
| 1 (1) | 2 (6, 1) | 8 |
| 2 (1, 2) | 1 (1) | 4 |
| 3 (1, 2, 3) | 0 | 6 |
Level III: Optimal (Sliding Window)
Intuition
Thought Process
Selecting k cards from the ends is equivalent to leaving n - k contiguous cards in the middle. To maximize the end cards, we must minimize the sum of the middle cards. This turns it into a fixed-size sliding window problem on the inner subarray.
Patterns
- Inverse Goal: Instead of picking ends, find the minimum middle.
- Fixed Window: The middle part always has size
n - k.
Detailed Dry Run
| Window | Sum | Min Sum | Action |
|---|---|---|---|
| [1, 2, 3, 4] | 10 | 10 | Initial |
| [2, 3, 4, 5] | 14 | 10 | Slide |
| [3, 4, 5, 6] | 18 | 10 | Slide |
| [4, 5, 6, 1] | 16 | 10 | Slide |
⚠️ Common Pitfalls & Tips
While you can use recursion with memoization, the sliding window approach is much faster and uses O(1) extra space. The 'inverse' trick is the most important leap.
Longest Subarray of 1's After Deleting One Element
Given a binary array nums, you must delete exactly one element. Return the maximum number of consecutive 1's remaining.
Visual Representation
nums = [1, 1, 0, 1]
[1, 1, 0, 1]
^ Delete this 0
Remaining: [1, 1, 1] -> Len 3
Sliding Window Rule:
Window must contain at most ONE zero.
Final Result = WindowSize - 1Examples
Level I: Brute Force
Intuition
Iterate through every element in the array. For each element, assume it is the one being deleted, and then find the longest contiguous block of 1's that can be formed using the remaining elements.
Thought Process
- Iterate through each index
ifrom0ton-1. - Simulate deleting
nums[i]. - After 'deletion', find the longest sequence of 1's in the remaining array.
- Track the maximum length found across all possible deletions.
Detailed Dry Run
| Deleting Index | Resulting Array | Max 1s | Result |
|---|---|---|---|
| 0 | [1, 0, 1] | 1 | 1 |
| 1 | [1, 0, 1] | 1 | 1 |
| 2 | [1, 1, 1] | 3 | 3 |
| 3 | [1, 1, 0] | 2 | 3 |
Level III: Optimal (Sliding Window)
Intuition
Thought Process
This is equivalent to 'Max Consecutive Ones III' with k = 1. We find the longest window with at most one zero. Since we must delete one element, the result is always WindowSize - 1.
Patterns
- Variable Window: Expand as long as zeros .
- Constraint: If
zeros > 1, shrink from left.
Detailed Dry Run
| R | Num | Zeros | Window Size | Result |
|---|---|---|---|---|
| 0 | 1 | 0 | 1 | 0 |
| 1 | 1 | 0 | 2 | 1 |
| 2 | 0 | 1 | 3 | 2 |
| 3 | 1 | 1 | 4 | 3 (MAX) |
⚠️ Common Pitfalls & Tips
If the array consists only of 1's, the result should be N - 1, not N. Don't forget that one deletion is MANDATORY.
Minimum Operations to Reduce X to Zero
Subtract values from either the left or right to reduce x exactly to 0. Return the minimum operations.
Visual Representation
nums = [1, 1, 4, 2, 3], x = 5
[1, 1] ... [3] (Total: 5, Ops: 3)
[1] ... [2, 3] (Total: 6, Ops: 3) X
... ... [2, 3] (Total: 5, Ops: 2) -> BEST
Goal: Maximize middle subarray with sum (TotalSum - x)
Middle Target = 11 - 5 = 6
Max Middle = [1, 1, 4] (Len 3)
Ops = 5 - 3 = 2Examples
Level I: Brute Force
Intuition
Iterate through all possible numbers of elements taken from the left end (i elements). For each i, find out how many elements from the right end are needed to reach exactly x. Track the minimum total operations.
Thought Process
- Let
ibe the number of elements from the left (0 <= i <= nums.length). - Calculate the sum of these
ielements. Ifsum > x, we can't take this many from left. - If
sum <= x, we need to findx - sumfrom the right end. - Use another loop (or map) to find how many elements from right are needed.
- Total operations =
i + count_from_right.
Detailed Dry Run
| Left Ops | Left Sum | Target Right | Right Ops | Total |
|---|---|---|---|---|
| 0 | 0 | 5 | 2 (2, 3) | 2 |
| 1 (1) | 1 | 4 | 0 (None) | - |
| 2 (1, 1) | 2 | 3 | 1 (3) | 3 |
Level III: Optimal (Sliding Window on Inverse Target)
Intuition
Thought Process
Finding minimum operations from the ends is the same as finding the maximum length contiguous subarray in the middle whose sum is totalSum - x. This simplifies the problem into a standard variable-size sliding window.
Patterns
- Inverse Target: Target sum for window =
Sum(nums) - x. - Result Mapping: Min operations from ends =
TotalLength - MaxWindowLength.
Detailed Dry Run
| Current Sum | Target | Max Len | Ops |
|---|---|---|---|
| 1 | 6 | - | - |
| 2 | 6 | - | - |
| 6 (Found!) | 6 | 3 | 5-3=2 |
| 8 | 6 | 3 | - |
⚠️ Common Pitfalls & Tips
Edge cases: x is larger than total sum (target < 0), or x is exactly the total sum (target = 0). Use long for sums to avoid overflow if arrays are very large.
Grumpy Bookstore Owner
A bookstore owner can be grumpy at certain minutes. You can use a 'secret technique' to keep them calm for minutes consecutive minutes. Maximize satisfied customers.
Visual Representation
customers = [1, 0, 1, 2, 1, 1, 7, 5]
grumpy = [0, 1, 0, 1, 0, 1, 0, 1]
minutes = 3
Base satisfaction (owner not grumpy): 1 + 1 + 1 + 7 = 10
Potential extra (using technique on window of size 3):
Win [1, 2, 1] (mins 1-3) -> Extra: 0*c[1] + 1*c[2] + 0*c[3] = 2
Win [7, 5] (mins 6-7) -> ... finding max extra ...Examples
Level I: Brute Force
Intuition
Iterate through every possible starting minute i for the 'X-minute technique'. For each i, calculate the total satisfied customers by summing: (base satisfied customers) + (additional customers satisfied while the technique is active at i).
Thought Process
- Calculate
BaseSatisfied: Sum ofcustomers[j]wheregrumpy[j] == 0for allj. - Iterate through each possible start
ifrom0ton - minutes. - For each
i, calculateAdditional: Sum ofcustomers[j]wheregrumpy[j] == 1andi <= j < i + minutes. - Max Score =
BaseSatisfied + max(Additional).
Detailed Dry Run
| Start | Base | Additional | Total |
|---|---|---|---|
| 0 | 10 | 1+1+0 | 12 |
| 3 | 10 | 2+1+1 | 14 |
| 5 | 10 | 1+5+0 | 16 (MAX) |
Level III: Optimal (Sliding Window)
Intuition
Thought Process
We want to choose a window of size minutes where we 'cancel' the owner's grumpiness to maximize additional customers. This is a classic fixed-size sliding window over the potential 'bonuses'.
Patterns
- Fixed Window: The technique duration is fixed at
minutes. - Additive Bonus: Base satisfied customers are always there; we only focus on maximizing the 'recovered' customers in a window.
Detailed Dry Run
| Window | Recovered | Max Recovered |
|---|---|---|
| [1,0,1] | 0 | 0 |
| [0,1,2] | 3 | 3 |
| [1,2,1] | 3 | 3 |
| [1,1,7] | 8 | 8 (MAX) |
⚠️ Common Pitfalls & Tips
Ensure you only add/remove customers[i] based on grumpy[i] == 1. The base customers grumpy[i] == 0 should not be affected by the sliding window logic after they are counted initially.
Maximum Number of Vowels in a Substring of Given Length
Find the maximum number of vowels in any substring of length k.
Visual Representation
s = "abciiidef", k = 3
Window [abc] -> Vowels: 1 (a)
Window [bci] -> Vowels: 1 (i)
Window [cii] -> Vowels: 2 (i, i)
Window [iii] -> Vowels: 3 (i, i, i) -> MAXExamples
Level I: Brute Force
Intuition
Iterate through all possible strings of length k. For each substring, count the number of vowels manually by iterating through characters.
Thought Process
- Iterate
ifrom0ton - k. - Extract substring
s[i : i+k]. - Count vowels in the substring.
- Track the maximum count observed.
Detailed Dry Run
| Substring | Vowels | Max |
|---|---|---|
| abc | a (1) | 1 |
| bci | i (1) | 1 |
| cii | i, i (2) | 2 |
| iii | i, i, i (3) | 3 |
Level II: Prefix Sums for Vowels (O(N) Space)
Intuition
We can precompute an array vowelCount where vowelCount[i] stores the number of vowels from index 0 to i-1. The number of vowels in any substring s[i...i+k-1] can be calculated as vowelCount[i+k] - vowelCount[i].
Thought Process
- Initialize an array
prefixof sizen + 1. - Iterate through the string; if
s[i]is a vowel,prefix[i+1] = prefix[i] + 1, otherwiseprefix[i+1] = prefix[i]. - Iterate
ifrom0ton - kand trackmax(prefix[i+k] - prefix[i]).
Pattern: Multi-pass Optimization
Level III: Optimal (Fixed Sliding Window)
Intuition
Thought Process
This is a classic fixed-size sliding window problem. We maintain a count of vowels. When a new character enters the window, we increment if it's a vowel. When a character leaves, we decrement if it was a vowel.
Patterns
- Fixed size
k: Window is always exactlykwide. - O(1) Update: Only the entering and leaving characters change the state.
Detailed Dry Run
| Window | Vowel Count | Max Count |
|---|---|---|
| [abc] | 1 | 1 |
| [bci] | 1 | 1 |
| [cii] | 2 | 2 |
| [iii] | 3 | 3 |
⚠️ Common Pitfalls & Tips
Be careful with string indexing. The check for leaving elements should happen once the index i is >= k.
Subarray Product Less Than K
Count the number of contiguous subarrays where the product of all elements is strictly less than k.
Visual Representation
nums = [10, 5, 2, 6], k = 100
[10] -> Prod: 10 (<100) Count +1
[10, 5] -> Prod: 50 (<100) Count +2 (new: [5], [10,5])
[10, 5, 2] -> Prod: 100 (>=100) -> Shrink left
[5, 2] -> Prod: 10 (<100) Count +2 (new: [2], [5,2])
Rule: Subarrays ending at 'right' = right - left + 1Examples
Level I: Brute Force
Intuition
Iterate through every possible starting point i and ending point j. For each pair, calculate the product of elements in nums[i...j]. If the product is less than k, increment the count.
Thought Process
- Outer loop
ifrom0ton-1. - Inner loop
jfromiton-1. - Calculate
prod = prod * nums[j]. - If
prod < k,count++. - If
prod >= k, break inner loop (since elements are positive, product will only increase).
Detailed Dry Run
| i | j | Prod | Result |
|---|---|---|---|
| 0 | 0 (10) | 10 | Count 1 |
| 0 | 1 (5) | 50 | Count 2 |
| 0 | 2 (2) | 100 | STOP |
| 1 | 1 (5) | 5 | Count 3 |
Level II: Log-Sum Prefix Sums + Binary Search (O(N log N))
Intuition
We can transform the product comparison into a sum comparison using logarithms: . Since all elements are positive, the prefix sums of are monotonically increasing, allowing us to use Binary Search for each starting point.
Thought Process
- Precompute an array
prefixLogswhereprefixLogs[i]is the sum oflog(nums[0...i-1]). - For each starting index
i, we need to find the largestjsuch thatprefixLogs[j+1] - prefixLogs[i] < log(K) - epsilon(to handle floating point precision). - The number of such
j's for a fixediisj - i + 1.
Pattern: Logarithmic Transformation
Level III: Optimal (Sliding Window)
Intuition
Thought Process
Standard variable-size sliding window. The key observation is that if a window [left, right] has a product < k, then ALL subarrays ending at right and starting between left and right also have products < k. There are right - left + 1 such subarrays.
Patterns
- Window-based Counting: Add
Right - Left + 1to result. - Product Control: Divide by
nums[left]when product .
Detailed Dry Run
| Right | Num | Product | Left | Count Add | Total |
|---|---|---|---|---|---|
| 0 | 10 | 10 | 0 | 1 | 1 |
| 1 | 5 | 50 | 0 | 2 | 3 |
| 2 | 2 | 100->10 | 1 | 2 | 5 |
| 3 | 6 | 60 | 1 | 3 | 8 |
⚠️ Common Pitfalls & Tips
Edge case k <= 1 is critical because the product of any subarray of positive integers is at least 1, which wouldn't be strictly less than 1.
Longest Turbulent Subarray
Find the maximum size of a 'turbulent' subarray where the comparison sign flips between adjacent pairs (e.g., a > b < c > d).
Visual Representation
arr = [9, 4, 2, 10, 7, 8, 8, 1, 9]
[9 > 4] (Turbulent? Yes, Len 2)
[9 > 4 < 2] (Turbulent? No, 4 > 2 is same sign as 9 > 4)
[2 < 10 > 7 < 8] (Turbulent? Yes, Len 4)
[8 == 8] (Reset window)Examples
Level I: Brute Force
Intuition
Iterate through all possible starting points i. For each i, expand the subarray as long as it remains turbulent. Track the maximum length.
Thought Process
- Outer loop
ifrom0ton-1. - Inner loop
jstarts fromi. - Check if
arr[j...j+1]satisfies the turbulent condition based onarr[j-1...j]. - If it breaks, record length and start next
i. - Maximize the findings.
Detailed Dry Run
| i | Turbulent Subarray | Length |
|---|---|---|
| 0 | [9, 4] | 2 |
| 1 | [4, 2] | 2 |
| 2 | [2, 10, 7, 8] | 4 |
| 6 | [8, 8] | 1 |
Level III: Optimal (Sliding Window)
Intuition
Thought Process
We essentially track the 'alternating sign' property. If the comparison between arr[i-1] and arr[i] matches the alternating expectation, we expand. If they are equal, we reset. If they have the same sign as the previous pair, we slide the start to the previous element.
Patterns
- Comparison Matching: Use
Integer.compareor sign functions. - Dynamic Anchor: Move anchor point based on where turbulence breaks.
Detailed Dry Run
| i | Comparison | anchor | res |
|---|---|---|---|
| 1 | 9 > 4 (1) | 0 | 2 |
| 2 | 4 > 2 (1) | 1 | 2 |
| 3 | 2 < 10 (-1) | 2 | 2 |
| 4 | 10 > 7 (1) | 2 | 3 |
| 5 | 7 < 8 (-1) | 2 | 4 |
⚠️ Common Pitfalls & Tips
Equal elements (e.g., 8, 8) break the turbulence immediately and reset the anchor to the current index.
Replace the Substring for Balanced String
Find the minimum length of a substring that can be replaced to make all characters ('Q', 'W', 'E', 'R') appear exactly n/4 times.
Visual Representation
s = "QQWE", n = 4, target = 1
Counts: Q:2, W:1, E:1, R:0
Over-limit: Q (2 > 1)
Goal: Find smallest window that contains the 'excess' characters.
Window [Q] (at index 0):
Remaining Counts outside window: Q:1, W:1, E:1, R:0
All remaining counts <= target? YES! (1, 1, 1, 0 <= 1)
Min Length = 1Examples
Level I: Brute Force
Intuition
Iterate through every possible substring that could be replaced. For each substring, calculate the character frequencies of the remaining part of the string. If all counts (Q, W, E, R) in the remaining part are , then the substring can be replaced to make the string balanced. Find the minimum length of such a substring.
Thought Process
- Outer loop
ifrom0ton-1. - Inner loop
jfromiton-1. - Count characters in
s[0...i-1]ands[j+1...n-1]. - If all counts ,
minLen = min(minLen, j - i + 1). - Handle the special case where the string is already balanced (length 0).
Detailed Dry Run
| Substring [i, j] | Remaining Counts | Valid? |
|---|---|---|
| Global | Q:2, W:1, E:1, R:0 | NO |
| [0, 0] (Q) | Q:1, W:1, E:1, R:0 | YES! (Len 1) |
| [1, 1] (Q) | Q:1, W:1, E:1, R:0 | YES! (Len 1) |
Level II: Binary Search on Window Size (O(N log N))
Intuition
We can binary search for the minimum length L of the substring to replace. For a fixed length L, we check if any substring of size L can be replaced to make the entire string balanced. A string is balanced if all counts of 'Q', 'W', 'E', 'R' are exactly . Replacing a substring means the counts of remaining characters must be .
Thought Process
- Target count for each character is
n/4. - In binary search, for a fixed
midlength:- Slide a window of size
midacross the string. - Count character frequencies outside the window.
- If all counts outside , then length
midis possible.
- Slide a window of size
- Adjust the search range for the minimum possible
mid.
Pattern: Search on Answer Space
Level III: Optimal (Sliding Window)
Intuition
Thought Process
A string is balanced if all counts are . To make a string balanced by replacing a substring, the characters outside that substring must all have counts . We search for the smallest window such that all characters remaining outside it satisfy this condition.
Patterns
- Window Complement: Check characters NOT in the window.
- Smallest Valid Window: Shrink from left while condition is met.
Detailed Dry Run
| Window | Count Q | Count W | Count E | Count R | Valid? |
|---|---|---|---|---|---|
| Global | 2 | 1 | 1 | 0 | NO |
| [Q] | 1 | 1 | 1 | 0 | YES |
| [QQ] | 0 | 1 | 1 | 0 | YES |
⚠️ Common Pitfalls & Tips
The goal is finding the smallest window that contains ALL excesses. If the string is already balanced, the answer is 0.
Get Equal Substrings Within Budget
Find the maximum length of a substring that can be transformed from s to t without exceeding maxCost.
Visual Representation
s = "abcd", t = "bcdf", maxCost = 3
Costs: |a-b|=1, |b-c|=1, |c-d|=1, |d-f|=2
Window [abc] -> [bcd]: Cost = 1+1+1 = 3 (<= 3) Len 3
Window [bcd] -> [cdf]: Cost = 1+1+2 = 4 (> 3) XExamples
Level I: Brute Force
Intuition
Iterate through all possible substrings s[i...j]. For each substring, calculate the transformation cost by summing the absolute differences between s[k] and t[k] for all k in the range. If the cost , track the length.
Thought Process
- Outer loop
ifrom0ton-1. - Inner loop
jfromiton-1. - Calculate
currentCost = sum(abs(s[k] - t[k]))forkfromitoj. - If
currentCost <= maxCost,maxLen = max(maxLen, j - i + 1).
Detailed Dry Run
| i | j | Substrings | Cost | Max Len |
|---|---|---|---|---|
| 0 | 0 | 'a'->'b' | 1 | 1 |
| 0 | 1 | 'ab'->'bc' | 1+1=2 | 2 |
| 0 | 2 | 'abc'->'bcd' | 1+1+1=3 | 3 |
| 0 | 3 | 'abcd'->'bcdf' | 3+2=5 | 3 |
Level II: Binary Search on Answer (O(N log N))
Intuition
We can binary search for the maximum possible length L of a valid substring. For a fixed length L, we use a sliding window of size L to check if at least one window satisfies the maxCost constraint.
Thought Process
- Search range for
Lis[0, n]. - In each step of binary search (length
mid):- Slide a window of size
midacross the strings. - Calculate the total cost of the window.
- If
totalCost <= maxCostfor any window, thenmidis possible.
- Slide a window of size
- Adjust the search range accordingly.
Pattern: Search on Answer Space
Level III: Optimal (Sliding Window)
Intuition
Thought Process
This is a standard variable-size sliding window problem on the array of transformation costs. We expand the window as long as the total cost is within the budget, and shrink from the left when it exceeds.
Patterns
- Rolling Cost Sum: Add cost of element at
Right. - Constraint Boundary: Move
Leftas long asSum > Budget. - Max Length Utility: Result is
max(Result, Right - Left + 1).
Detailed Dry Run
| Right | Cost | Total Cost | Left | Max Len |
|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 1 |
| 1 | 1 | 2 | 0 | 2 |
| 2 | 1 | 3 | 0 | 3 |
| 3 | 2 | 5 -> 4 | 1 | 3 |
⚠️ Common Pitfalls & Tips
Be mindful of binary characters vs ASCII values. Use abs(ord(s[i]) - ord(t[i])) in Python or Math.abs(s.charCodeAt(i) - t.charCodeAt(i)) in JS.
Subarrays with K Different Integers
Count the number of 'good' subarrays where the number of different integers is exactly k.
Visual Representation
nums = [1, 2, 1, 2, 3], k = 2
Exactly(K) = AtMost(K) - AtMost(K-1)
AtMost(2) Subarrays: [1], [2], [1,2], [1,2,1], [2,1,2] ...
AtMost(1) Subarrays: [1], [2], [1], [2], [3]
Exactly(2) = 12 - 5 = 7Examples
Level I: Brute Force
Intuition
Iterate through all possible substrings nums[i...j]. For each substring, count the number of unique integers. If the count is exactly k, increment the total result.
Thought Process
- Outer loop
ifrom0ton-1. - Inner loop
jfromiton-1. - Maintain a set of unique elements encountered so far in
nums[i...j]. - If
set.size() == k,res++. - If
set.size() > k, break inner loop (since distinct elements only increase).
Detailed Dry Run
| i | j | Subarray | Distinct | Result |
|---|---|---|---|---|
| 0 | 0 | [1] | 1 | 0 |
| 0 | 1 | [1, 2] | 2 | 1 |
| 0 | 2 | [1, 2, 1] | 2 | 2 |
| 0 | 4 | [1, 2, 1, 2, 3] | 3 | STOP |
Level II: Sliding Window with 3 Pointers (O(N))
Intuition
We can use three pointers to find the number of subarrays with exactly k distinct integers directly. We maintain two 'left' pointers: left1 (minimum index to satisfy k distinct) and left2 (minimum index to satisfy k-1 distinct). The number of valid subarrays ending at right is left2 - left1.
Thought Process
- Maintain
count1(frequencies forleft1) andcount2(frequencies forleft2). - As we expand
right, update both maps. - Move
left1as far as possible such that the window[left1, right]haskdistinct elements. - Move
left2as far as possible such that the window[left2, right]hask-1distinct elements. - For each
right, any start index in the range[left1, left2)produces a valid subarray.
Pattern: Multi-pointer Sliding Window
Level III: Optimal (Sliding Window - Two Pass)
Intuition
Thought Process
Finding 'exactly K' is difficult with a single sliding window because shrinking doesn't always reduce the count of distinct elements. However, finding 'at most K' is a standard sliding window problem. Using the property Exactly(K) = AtMost(K) - AtMost(K-1) simplifies the problem significantly.
Patterns
- Complement Rule:
Exactly(K) = AtMost(K) - AtMost(K-1). - Frequency Map: Track counts of each number in the current window.
Detailed Dry Run
| X | AtMost(X) |
|---|---|
| 2 | 12 |
| 1 | 5 |
| Result | 12 - 5 = 7 |
⚠️ Common Pitfalls & Tips
Be careful with the count map logic. Incrementing AtMost(K) means counting ALL subarrays with distinct elements.
Frequency of the Most Frequent Element
Maximize the frequency of an element by incrementing other elements by at most k overall.
Visual Representation
nums = [1, 2, 4], k = 5
Sort: [1, 2, 4]
Window [1, 2, 4] ending at 4:
Cost to make all 4: (4-1) + (4-2) + (4-4) = 3 + 2 + 0 = 5
Cost 5 <= k (5)? YES! Frequency = 3Examples
Level I: Brute Force
Intuition
For each element x in the array, try to make a sequence of elements equal to x ending at x. Use a sliding window to find the maximum possible frequency for each starting position.
Thought Process
- Sort the array.
- Iterate
ifrom0ton-1. - Iterate
jfromiton-1. - Calculate
cost = sum(nums[j] - nums[k])forkin[i...j]. - If
cost <= k,maxFreq = max(maxFreq, j - i + 1).
Detailed Dry Run
| i | j | Window | Cost | Max Freq |
|---|---|---|---|---|
| 0 | 0 | [1] | 0 | 1 |
| 0 | 1 | [1, 2] | (2-1) = 1 | 2 |
| 0 | 2 | [1, 2, 4] | (4-1)+(4-2) = 5 | 3 |
Level II: Binary Search on Answer (O(N log N))
Intuition
After sorting, we can binary search for the maximum frequency L. For a fixed L, we check if any subarray of length L exists such that the cost to make all elements equal to the largest element in that subarray is .
Thought Process
- Sort the array.
- Search range for frequency
Lis[1, n]. - For each
midlength:- Use a fixed-size sliding window of size
mid. - Cost for window
[i, i + mid - 1]isnums[i + mid - 1] * mid - windowSum. - If any window has
cost <= k,midis possible.
- Use a fixed-size sliding window of size
- Adjust the search range accordingly.
Pattern: BS on Answer + Rolling Sum
Level III: Optimal (Sorting + Sliding Window)
Intuition
Thought Process
After sorting, for any window [left, right], it's always optimal to make all elements equal to the largest element nums[right]. The total cost is nums[right] * windowSize - windowSum. We maintain the largest possible window that satisfies cost <= k.
Patterns
- Sort then Window: Sorting allows monotonic cost calculations.
- Sum-based Cost:
cost = lastVal * len - sum.
Detailed Dry Run
| R | Num | Sum | Cost (4*Win - Sum) | Max Freq |
|---|---|---|---|---|
| 0 | 1 | 1 | 0 | 1 |
| 1 | 2 | 3 | 2*2 - 3 = 1 | 2 |
| 2 | 4 | 7 | 4*3 - 7 = 5 | 3 |
⚠️ Common Pitfalls & Tips
Integer overflow is a common issue here. nums[right] * (right - left + 1) can exceed 2^31 - 1, so use 64-bit integers (long in Java/C++, default in Python/JS) for intermediate calculations.
Longest Substring with At Most K Distinct Characters
Given a string s and an integer k, return the length of the longest substring that contains at most k distinct characters.
Visual Representation
s = "eceba", k = 2
[e]
[e, c]
[e, c, e] (Distinct: 2 <= 2, Valid!)
[e, c, e, b] (Distinct: 3 > 2, INVALID!)Examples
Level I: Brute Force
Intuition
Check every possible substring and count unique characters. Update maximum length if the count is .
Level III: Optimal (Sliding Window)
Intuition
Thought Process
Standard variable-size sliding window. Maintain a frequency map of characters in the window. When map size exceeds k, shrink from the left.
Pattern: Variable Size Sliding Window (K Distinct)
Count Complete Subarrays in an Array
A subarray is complete if the number of distinct elements in it is equal to the number of distinct elements in the entire array.
Visual Representation
nums = [1, 3, 1, 2, 2], Total Distinct: 3
[1, 3, 1, 2] (Distinct: 3, VALID!)
[3, 1, 2] (Distinct: 3, VALID!)
[1, 2, 2] (Distinct: 2, INVALID)Examples
Level I: Brute Force
Intuition
Find the total number of distinct elements in the array first. Then, check every subarray and count how many are complete.
Level III: Optimal (Sliding Window)
Intuition
Thought Process
This is equivalent to finding subarrays with at least k distinct elements. We can use the 'at least' pattern or the 'total - (at most k-1)' pattern.
Calculation
Total Subarrays - Subarrays with at most (K-1) distinct elements.
Found an issue or have a suggestion?
Help us improve! Report bugs or suggest new features on our Telegram group.