Pattern Recognition — Variable-Size Sliding Window (LC76)

1. Identifying the Sliding Window Pattern

In interviews, the first step is spotting that the problem can be solved using a sliding window:

  • You're asked for a **substring/subarray** in s.
  • There’s a **condition based on counts or presence of elements** (like all characters of t must be included).
  • The window length is **not fixed**, so you need to dynamically expand and shrink it.

2. ASCII Flow Example

Let's visualize pointer movement:

s = "ADOBECODEBANC"
t = "ABC"

Start with empty window:
[start:end] -> ""
 ^

Expand end until all t chars present:
[start:end] -> "ADOBEC"
 ^     ^

Shrink start to minimize:
[start:end] -> "DOBEC"
  ^   ^

Shrink further:
[start:end] -> "BANC"
     ^   ^
  

Notice how the end pointer moves right to include all required chars, and start pointer moves right to remove unnecessary characters.

3. Step-by-Step Recognition Checklist

  • Are we asked for the **smallest/largest substring** meeting some condition? ✅
  • Do we need to **track counts or frequency** of characters? ✅
  • Should the window **expand and shrink dynamically**? ✅
  • If yes to all, this is a **variable-size sliding window problem**.

4. Comparison with Two Sum / 3Sum Patterns

Problem Pattern Type Pointers Window/Segment
Two Sum (Unsorted) Hash Map / One-pass search Single pass, check complement No explicit window
Two Sum II (Sorted) Two Pointers Left & right pointers converge Effectively a shrinking window
3Sum Two Pointers inside loop Left & right pointers per base index Fixed-size sum window per iteration
Minimum Window Substring (LC76) Variable-Size Sliding Window Start & End expand/shrink dynamically Dynamic substring containing all required chars

5. Beginner-Friendly Tips

  • Draw the string and mark start/end pointers.
  • Maintain a **character count map** to check if the window is valid.
  • Think in terms of **expand → check → shrink → record answer → repeat**.
  • Compare with Two Sum II: here, instead of sum equality, the condition is "all chars of t are included".

6. Key Keywords That Hint Variable-Size Sliding Window

Look for phrases in problem statements:

  • "smallest substring"
  • "contains all characters"
  • "minimum length window"
  • "all elements present"

Interview-Focused — Short Answer to Deliver (Two Sum)

"Start with a brute-force to show correctness (two nested loops). Then use a one-pass hash map: for each element compute complement = target - x, check map for complement, return indices if found; otherwise store x -> index. This gives O(n) time and O(n) space. Check duplicates and edge cases; if input were sorted, we could use two pointers instead."

One-minute checklist to say

  • State brute and why it's slow.
  • State hash map approach and complexity.
  • Do a short dry-run on sample input.
  • Mention edge cases and follow-ups.

Interview Script & What to Say (Two Sum)

Step-by-step script (word-for-word suggestions)

  1. "I'll restate the problem: given nums and target, return indices of two numbers that add up to target. Are the nums sorted and should I return indices or values?"
  2. "A brute-force approach checks all pairs in O(n²). This is correct but too slow for large n."
  3. "A better approach is to use a hash map storing value→index. For each x, compute complement = target - x. If the complement exists in the map, return the stored index and current index; otherwise insert x into the map. This is O(n) time and O(n) space."
  4. Dry-run a small example out loud (e.g., [2,7,11,15],9)."
  5. "Edge cases: duplicates like [3,3] handled by checking complement before insert, negative numbers are fine, minimal size 2, and some variants may not guarantee a solution."

What the interviewer hears

  • Clear problem understanding and constraints checking.
  • Ability to articulate a baseline and then optimize with correct data structure choice.
  • Concise complexity analysis and edge-case coverage.

Solution Approach — Variable-Size Sliding Window (LC76)

1. Problem Restatement

Find the smallest substring in s that contains all characters of t. Input: s = "ADOBECODEBANC", t = "ABC" → Output: "BANC".

2. Core Idea

  • Use **two pointers**: start and end.
  • Move end to expand the window until it contains all required characters.
  • Move start to shrink the window as much as possible while still satisfying the condition.
  • Keep track of the **minimum-length window** seen so far.

3. ASCII Pointer Flow

s = "ADOBECODEBANC"
t = "ABC"
map = {A:1, B:1, C:1}

Window [start:end]:
Step 1: ""               start=0 end=0
Step 2: "A"              start=0 end=0 -> satisfies? No
Step 3: "AD"             start=0 end=1 -> No
Step 4: "ADO"            start=0 end=2 -> No
Step 5: "ADOB"           start=0 end=3 -> No
Step 6: "ADOBE"          start=0 end=4 -> No
Step 7: "ADOBEC"         start=0 end=5 -> Yes! (contains A,B,C)
Shrink start to minimize:
Window -> "DOBEC"         start=1 end=5
Window -> "OBEC"          start=2 end=5
Window -> "BEC"           start=3 end=5 -> still valid, record min length
Continue expanding end:
Window -> "BECODEBANC"    end moves until string ends
Shrink start dynamically, record "BANC" as min
  

4. JavaScript Step-by-Step Dry Run


// Initialize maps
let need = {A:1, B:1, C:1};
let window = {};
let have = 0; // count of satisfied chars
let required = Object.keys(need).length;
let start = 0, minLen = Infinity, minStart = 0;

for (let end = 0; end < s.length; end++) {
  let char = s[end];
  window[char] = (window[char] || 0) + 1;

  // If char count matches need, increment have
  if (need[char] && window[char] === need[char]) have++;

  // When all required chars are satisfied, shrink start
  while (have === required) {
    // Update minimum window
    if (end - start + 1 < minLen) {
      minLen = end - start + 1;
      minStart = start;
    }

    // Remove start char from window
    let startChar = s[start];
    window[startChar]--;
    if (need[startChar] && window[startChar] < need[startChar]) have--;
    start++;
  }
}

let result = minLen === Infinity ? "" : s.substring(minStart, minStart + minLen);
console.log(result); // "BANC"
  

5. Step-by-Step Explanation

  1. Expand end until the window contains all required characters from t.
  2. Check if the current window is smaller than the previous minimum. If yes, update minLen and minStart.
  3. Shrink the window from start to discard unnecessary characters while keeping all required characters.
  4. Repeat until end reaches the string’s end.
  5. Return the minimum-length substring recorded.

6. Key Beginner Takeaways

  • Track **counts of each character** using a map.
  • Always **expand end** first, then **shrink start** dynamically.
  • Maintain a **running count of satisfied characters** to know when the window is valid.
  • Compare with Two Sum II: here, instead of sum equality, the condition is "all required characters present".

Deep Dive — Variable-Size Sliding Window (LC76)

1. Correctness Invariant

At any moment, the window defined by start..end contains all characters in t that satisfy the count requirement. Invariant: have === required whenever the window is valid.

Shrinking from start never invalidates the invariant until have < required. Expanding end increases coverage toward satisfying all required characters.

2. Why Expand-End First & Shrink-Start Later

  • Expanding first ensures we include enough characters to satisfy t.
  • Shrinking afterward guarantees minimal-length window while keeping the invariant valid.
  • Switching the order risks missing the minimum window or discarding required characters prematurely.

3. Variants & Trade-Offs

  • Find all substrings: Instead of recording just minimum, collect all valid windows — may increase space complexity.
  • Streaming input: Keep dynamic counts in a hash map; shrink window using a queue if input is unbounded.
  • Unicode / large charset: Using a fixed-size array may not work; hash map provides O(1) amortized access.
  • Different constraints: Minimum window with extra conditions, e.g., unique chars only — adjust map logic accordingly.

4. Micro-Optimizations

  • Use a single map for need and window if memory is tight.
  • Check have === required before shrinking; avoids unnecessary iterations.
  • Record window length only when have === required; prevents invalid windows from polluting results.
  • If string is long but charset small (e.g., only uppercase letters), consider array-based direct addressing.

5. Proof Sketch

Let [i, j] be any valid minimal-length window. Our algorithm expands end until all required characters are present, then shrinks start as much as possible. Since we record only windows that satisfy have === required, the minimal-length window is guaranteed to be captured. No valid window is skipped because shrinking stops exactly when removing any further character would violate have === required.

6. Beginner Takeaways

  • Invariant tracking is the key: have vs required.
  • Always separate **expand-end** logic from **shrink-start** logic.
  • ASCII flow and dry-run in your mind to visualize pointer movement.
  • Compare with Two Sum II / 3Sum: Instead of sum, condition is “all characters covered”. The pattern of sliding window pointers is consistent.

Common Pitfalls — Variable-Size Sliding Window

1. Shrinking the window too early

A very common mistake is moving start before all required characters are included. This leads to invalid windows being considered as valid.

Example:
s = "ADOBECODEBANC", t = "ABC"
Window moves: [A] → [AD] → [ADO] → ... shrink too early at [ADOB]
Result: missing minimal window or invalid substring
  

2. Miscounting characters in the window

Forgetting to decrement the have count when shrinking or not tracking duplicate characters properly often causes incorrect results.

Example:
Window contains "AABC", t="ABC"
If removing one 'A' reduces count below required, forgetting to update have may still mark window as valid.
  

3. Using wrong data structure for counting

Beginners sometimes use arrays for character counts, but if the input includes mixed-case letters or Unicode, it can break. Hash maps provide a safer, dynamic approach.

4. Not updating the minimal window properly

Failing to record the minimal length and start index each time the window is valid leads to returning suboptimal or wrong results.

5. Pointer off-by-one errors

Off-by-one mistakes when shrinking start or expanding end are extremely common. Dry-run with ASCII pointer visualization helps prevent these.

6. Comparing with Two Sum / 3Sum mistakes

Unlike Two Sum or 3Sum where we check sums, here the condition is “all characters covered”. Treating the sliding window like a sum-based check may mislead beginners; always track have === required invariant.

Beginner Tips

  • Always dry-run on a short string to track have and required.
  • Use ASCII diagrams to visualize expand/shrink pointer behavior.
  • Be cautious with duplicates in t — they must be fully counted.
  • Maintain minimal window length and start index carefully each time have === required.

Edge Cases — Variable-Size Sliding Window

1. Minimal input

When s or t is very short, ensure your code still handles it.

Example:
s = "A", t = "A" → window = "A"
s = "A", t = "B" → no valid window
  

2. Empty string or pattern

Always check if s or t is empty to avoid errors.

s = "", t = "A" → return ""
s = "A", t = "" → return ""
  

3. Characters not present in string

When t contains characters absent in s, the function should return an empty string.

s = "ADOBECODEBANC", t = "XYZ" → return ""
  

4. Multiple minimal windows

Sometimes more than one window has the same minimal length. Track first occurrence or last occurrence based on requirement.

s = "ADOBECODEBANC", t = "ABC"
Valid minimal windows: "BANC" only
s = "AAABBBCCC", t = "ABC"
Windows of same minimal length: "ABC", "ABC", ...
  

5. All characters consecutive vs scattered

Both dense and scattered patterns should be handled.

s = "ABC" → all together, minimal window = "ABC"
s = "AxxBxxC" → scattered, minimal window = "AxxBxxC"
ASCII Pointer Visualization:
[start] A x x B x x C [end]
Shrink carefully only when all characters are covered
  

6. Case sensitivity

Sliding window is case-sensitive by default. 'A' ≠ 'a'.

s = "aA", t = "A" → window = "A"
s = "aA", t = "a" → window = "a"
  

Interview Demonstration Tips

  • Use ASCII pointer diagrams on the whiteboard to show expand/shrink mechanics.
  • Run a minimal example live (like s = "ADOBEC", t = "ABC") to demonstrate dynamic updates.
  • Highlight edge-case checks explicitly to show interviewer you consider all scenarios.
  • Explain why tracking have === required is crucial in all these edge cases.

Time & Space Complexity — Two Sum

Understanding time and space complexity is critical for interviews. Here we break down each approach with detailed reasoning and examples using JavaScript.

1. Brute-force approach

Check all pairs using nested loops. For array nums of length n:

for i = 0 to n-2:
    for j = i+1 to n-1:
        if nums[i] + nums[j] == target -> return [i,j]
    
  • Time complexity: O(n²) — every element pairs with every other.
  • Space complexity: O(1) — no extra data structures.
  • When to mention in interviews: Demonstrates correctness but is inefficient for large n.

2. Sorting + two-pointer approach

Applicable if array is sorted or you only need values (not indices). Steps in JavaScript:

JavaScript — Two-pointer example

const nums = [2,7,11,15].sort((a,b)=>a-b);
let left = 0, right = nums.length-1;

while(left < right) {
  const sum = nums[left] + nums[right];
  if(sum === target) return [left,right]; // values, map back if needed
  else if(sum < target) left++;
  else right--;
}
    
  • Time complexity: O(n log n) due to sorting; O(n) for pointer traversal.
  • Space complexity: O(n) if you need to map sorted indices back; O(1) if values-only.
  • When to mention: Useful if interviewer allows sorting or when memory is tight.

3. One-pass hash map — optimal

Maintains a map of previously seen numbers to their indices for O(1) complement lookup.

nums = [2,7,11,15], target = 9
seen = {} // map of value -> index

i=0: x=2, complement=7 -> not in seen -> store 2:0
seen = {2:0}

i=1: x=7, complement=2 -> found in seen -> return [0,1]
    
  • Time complexity: O(n) — each element processed once, map lookup is O(1) on average.
  • Space complexity: O(n) — stores each unique number in the map.
  • Edge discussion: Worst-case hash collisions exist but are rare; acceptable for interviews.
  • JavaScript note: Use Map for predictable iteration and key handling.

Memory usage example

For nums = [1,2,3,4,5], map stores up to 5 key-value pairs:

Index: 0 1 2 3 4
Nums : 1 2 3 4 5
Map after processing:
{1:0}, {1:0,2:1}, {1:0,2:1,3:2}, ... etc
    

Summary table

ApproachTimeSpaceNotes
Brute-force O(n²) O(1) Simple but inefficient; good to explain correctness.
Sort + two-pointer O(n log n) O(n) for indices, O(1) for values-only Useful if memory constrained; map indices if needed.
One-pass hash map O(n) average O(n) Default optimal choice; mention collisions and edge cases in interviews.

Interview tips

  • Always articulate why you picked an approach — time vs space trade-off.
  • Show dry-run with a small array to demonstrate memory usage and operations.
  • If interviewer asks about worst-case memory, explain hash map growth and alternatives like two-pointer if allowed.
  • Explicitly mention O(1) extra space for brute-force and two-pointer (values-only).

Language-Specific Notes

Implementation details for the fixed sliding window solution vary by language. Select to view optimizations.

FAQs — Variable-Size Sliding Window (LC76)

1. What is a variable-size sliding window?

A variable-size sliding window dynamically expands and shrinks as you iterate over the string or array. It's used when the target condition depends on a subset of elements rather than a fixed-length window.

Example:
s = "ADOBECODEBANC", t = "ABC"
Window expands until all chars in t are included, then shrinks to find minimal valid substring.
  

2. How do you know when to shrink the window?

Shrink the window from the left as long as all required characters are still covered. Use a counter like have === required to ensure validity.

3. Common interview micro-optimizations

  • Use a frequency map instead of scanning substring repeatedly.
  • Track only counts of needed characters, ignore extras.
  • Early exit if minimal possible window is reached.
  • Use two pointers (left/right) and update result only when valid.

4. How to explain this approach in interviews?

  1. Restate problem and confirm understanding of "substring containing all chars".
  2. Show brute-force idea: check all substrings → O(n²) or O(n³) depending on checks.
  3. Introduce variable-size sliding window as optimization → O(n) or O(n + |t|) with maps.
  4. Walk through a dry-run example (like s = "ADOBECODEBANC", t = "ABC") using ASCII pointer diagrams.
  5. Explain edge cases: minimal input, empty strings, scattered characters, duplicates, case sensitivity.

5. How to handle duplicates in target string?

Track the required count for each character. Example: t = "AABC", frequency map = {A:2, B:1, C:1}. Window is valid only when counts inside window ≥ required counts.

6. What are common mistakes to avoid?

  • Shrinking too early before satisfying all required characters.
  • Not updating counts correctly when expanding or shrinking.
  • Ignoring case sensitivity if specified.
  • Returning window before checking all characters are included.

7. Related patterns & problem hints

  • Variable-size sliding window is used in problems like minimum window substring, substring with K distinct chars, longest substring with at most K repeating chars.
  • Clues in interviews: "smallest substring", "contains all", "at least / exactly X elements".

8. Keywords interviewers look for

Phrases like "expand/shrink window", "dynamic substring", "frequency map", "two pointers", "minimal valid window" often hint at this pattern.