Bird
Raised Fist0
Interview Prepchallenge-problemshardAmazonFacebookGoogleMicrosoftLinkedIn

Minimum Window Substring

Choose your preparation mode4 modes available
🎯
Minimum Window Substring
hardMIXEDAmazonFacebookGoogle

Imagine you are searching through a large document to find the shortest snippet that contains all the keywords you need for your research.

💡 This problem requires finding a substring that contains all characters of another string, which is tricky because beginners often struggle with efficiently tracking character frequencies and managing dynamic window sizes.
📋
Problem Statement

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. If there is no such substring, return the empty string "".

1 ≤ s.length, t.length ≤ 10^5s and t consist of English letters.
💡
Example
Input"s = \"ADOBECODEBANC\", t = \"ABC\""
Output"BANC"

The substring "BANC" contains all characters A, B, and C and is the shortest such substring.

  • t is longer than s → ""
  • s and t are identical → s
  • t contains characters not in s → ""
  • s or t is empty → ""
⚠️
Common Mistakes
Not checking if the window contains all required characters before shrinking

Returns incorrect or empty substring

✅ Use a 'have' counter to track when all required characters are included before shrinking

Using hash maps but forgetting to decrement counts when moving left pointer

Window counts become inaccurate, leading to incorrect results

✅ Always decrement the count of the character being removed from the window

Not handling characters in s that are not in t

Unnecessary shrinking or expansion, inefficient code

✅ Only update 'have' counter and check validity for characters present in t

Returning the substring using wrong indices after the loop

Off-by-one errors or empty string returned incorrectly

✅ Store start and end indices carefully and return substring accordingly

Using nested loops in brute force without pruning

Code times out on large inputs

✅ Explain brute force only conceptually and move to sliding window for coding

🧠
Brute Force (Nested Loops Checking All Substrings)
💡 Starting with brute force helps understand the problem deeply by checking every possible substring, even though it's inefficient.

Intuition

Check every substring of s and verify if it contains all characters of t by counting frequencies.

Algorithm

  1. Generate all possible substrings of s.
  2. For each substring, count the frequency of characters.
  3. Check if the substring contains all characters of t with required frequency.
  4. Keep track of the minimum length substring that satisfies the condition.
💡 The nested loops and frequency checks make this approach easy to understand but hard to optimize without further insight.
</>
Code
def min_window_brute_force(s: str, t: str) -> str:
    from collections import Counter
    if not s or not t:
        return ""
    t_count = Counter(t)
    required = len(t_count)
    min_len = float('inf')
    min_window = ""
    for i in range(len(s)):
        for j in range(i, len(s)):
            window = s[i:j+1]
            window_count = Counter(window)
            if all(window_count[c] >= t_count[c] for c in t_count):
                if j - i + 1 < min_len:
                    min_len = j - i + 1
                    min_window = window
    return min_window

# Example usage:
if __name__ == "__main__":
    print(min_window_brute_force("ADOBECODEBANC", "ABC"))  # Output: BANC
Line Notes
from collections import CounterImport Counter to easily count character frequencies, which simplifies frequency comparisons.
if not s or not t:Handle edge cases where input strings are empty to avoid unnecessary computation.
for i in range(len(s))Outer loop selects the start index of the substring to consider.
for j in range(i, len(s))Inner loop selects the end index of the substring, generating all substrings.
window_count = Counter(window)Count characters in the current substring to compare with required frequencies.
if all(window_count[c] >= t_count[c] for c in t_count)Check if current substring contains all required characters with correct frequency.
if j - i + 1 < min_lenUpdate minimum window if current substring is smaller than previously found ones.
import java.util.*;
public class MinWindowSubstringBrute {
    public static String minWindow(String s, String t) {
        if (s == null || t == null || s.length() == 0 || t.length() == 0) return "";
        Map<Character, Integer> tCount = new HashMap<>();
        for (char c : t.toCharArray()) tCount.put(c, tCount.getOrDefault(c, 0) + 1);
        int minLen = Integer.MAX_VALUE;
        String minWindow = "";
        for (int i = 0; i < s.length(); i++) {
            for (int j = i; j < s.length(); j++) {
                String window = s.substring(i, j + 1);
                Map<Character, Integer> windowCount = new HashMap<>();
                for (char c : window.toCharArray()) windowCount.put(c, windowCount.getOrDefault(c, 0) + 1);
                boolean valid = true;
                for (char c : tCount.keySet()) {
                    if (windowCount.getOrDefault(c, 0) < tCount.get(c)) {
                        valid = false;
                        break;
                    }
                }
                if (valid && window.length() < minLen) {
                    minLen = window.length();
                    minWindow = window;
                }
            }
        }
        return minWindow;
    }
    public static void main(String[] args) {
        System.out.println(minWindow("ADOBECODEBANC", "ABC")); // Output: BANC
    }
}
Line Notes
if (s == null || t == null || s.length() == 0 || t.length() == 0)Check for empty or null inputs early to avoid unnecessary processing.
for (char c : t.toCharArray())Count frequency of characters in t to know requirements.
for (int i = 0; i < s.length(); i++)Outer loop selects start index of substring.
for (int j = i; j < s.length(); j++)Inner loop selects end index of substring.
Map<Character, Integer> windowCount = new HashMap<>();Count characters in current substring for frequency comparison.
if (windowCount.getOrDefault(c, 0) < tCount.get(c))Check if current substring lacks required characters.
if (valid && window.length() < minLen)Update minimum window if current substring is valid and smaller.
#include <iostream>
#include <string>
#include <unordered_map>
using namespace std;

string minWindowBrute(string s, string t) {
    if (s.empty() || t.empty()) return "";
    unordered_map<char, int> tCount;
    for (char c : t) tCount[c]++;
    int minLen = INT_MAX;
    string minWindow = "";
    for (int i = 0; i < (int)s.size(); i++) {
        for (int j = i; j < (int)s.size(); j++) {
            unordered_map<char, int> windowCount;
            for (int k = i; k <= j; k++) windowCount[s[k]]++;
            bool valid = true;
            for (auto &p : tCount) {
                if (windowCount[p.first] < p.second) {
                    valid = false;
                    break;
                }
            }
            if (valid && j - i + 1 < minLen) {
                minLen = j - i + 1;
                minWindow = s.substr(i, minLen);
            }
        }
    }
    return minWindow;
}

int main() {
    cout << minWindowBrute("ADOBECODEBANC", "ABC") << endl; // Output: BANC
    return 0;
}
Line Notes
if (s.empty() || t.empty()) return "";Handle empty input strings immediately to avoid unnecessary computation.
for (char c : t) tCount[c]++;Count frequency of characters in t to know requirements.
for (int i = 0; i < (int)s.size(); i++)Outer loop selects start index of substring.
for (int j = i; j < (int)s.size(); j++)Inner loop selects end index of substring.
for (int k = i; k <= j; k++) windowCount[s[k]]++;Count characters in current substring for frequency comparison.
if (windowCount[p.first] < p.second)Check if current substring lacks required characters.
if (valid && j - i + 1 < minLen)Update minimum window if current substring is valid and smaller.
function minWindowBruteForce(s, t) {
    if (!s || !t) return "";
    const tCount = {};
    for (const c of t) tCount[c] = (tCount[c] || 0) + 1;
    let minLen = Infinity;
    let minWindow = "";
    for (let i = 0; i < s.length; i++) {
        for (let j = i; j < s.length; j++) {
            const window = s.substring(i, j + 1);
            const windowCount = {};
            for (const c of window) windowCount[c] = (windowCount[c] || 0) + 1;
            let valid = true;
            for (const c in tCount) {
                if (!windowCount[c] || windowCount[c] < tCount[c]) {
                    valid = false;
                    break;
                }
            }
            if (valid && window.length < minLen) {
                minLen = window.length;
                minWindow = window;
            }
        }
    }
    return minWindow;
}

// Example usage:
console.log(minWindowBruteForce("ADOBECODEBANC", "ABC")); // Output: BANC
Line Notes
if (!s || !t) return "";Return empty string if inputs are empty or null to handle edge cases.
for (const c of t) tCount[c] = (tCount[c] || 0) + 1;Count frequency of characters in t to know requirements.
for (let i = 0; i < s.length; i++)Outer loop selects start index of substring.
for (let j = i; j < s.length; j++)Inner loop selects end index of substring.
for (const c of window) windowCount[c] = (windowCount[c] || 0) + 1;Count characters in current substring for frequency comparison.
if (!windowCount[c] || windowCount[c] < tCount[c])Check if current substring lacks required characters.
if (valid && window.length < minLen)Update minimum window if current substring is valid and smaller.
⏱
Complexity
TimeO(n^3)
SpaceO(n)

There are O(n^2) substrings and counting frequencies for each substring takes O(n), resulting in O(n^3) time.

💡 For n=20, this means roughly 8000 operations, which is slow but manageable; for n=1000, it becomes impractical.
Interview Verdict: TLE

This approach is too slow for large inputs but is useful to understand the problem and correctness before optimizing.

🧠
Sliding Window with Frequency Counting
💡 This approach introduces the sliding window technique to reduce redundant checks and improve efficiency by dynamically adjusting the window size.

Intuition

Expand the right pointer to include characters until all required are found, then move the left pointer to minimize the window while maintaining validity.

Algorithm

  1. Count the frequency of characters in t.
  2. Use two pointers (left and right) to represent the current window in s.
  3. Expand right pointer to include characters until the window contains all characters of t.
  4. Shrink left pointer to remove unnecessary characters and update minimum window.
  5. Repeat until right pointer reaches the end of s.
💡 The challenge is correctly tracking when the window is valid and efficiently updating counts while moving pointers.
</>
Code
def min_window_sliding_window(s: str, t: str) -> str:
    from collections import Counter
    if not s or not t:
        return ""
    t_count = Counter(t)
    window_count = {}
    have, need = 0, len(t_count)
    res, res_len = [-1, -1], float('inf')
    left = 0
    for right, c in enumerate(s):
        window_count[c] = window_count.get(c, 0) + 1
        if c in t_count and window_count[c] == t_count[c]:
            have += 1
        while have == need:
            if (right - left + 1) < res_len:
                res = [left, right]
                res_len = right - left + 1
            window_count[s[left]] -= 1
            if s[left] in t_count and window_count[s[left]] < t_count[s[left]]:
                have -= 1
            left += 1
    left, right = res
    return s[left:right+1] if res_len != float('inf') else ""

# Example usage:
if __name__ == "__main__":
    print(min_window_sliding_window("ADOBECODEBANC", "ABC"))  # Output: BANC
Line Notes
from collections import CounterUse Counter to count characters in t efficiently, simplifying frequency tracking.
t_count = Counter(t)Store required character frequencies to know what to match.
window_count = {}Track frequencies of characters in the current window dynamically.
have, need = 0, len(t_count)Track how many unique required characters are currently satisfied.
for right, c in enumerate(s)Expand window by moving right pointer through s.
if c in t_count and window_count[c] == t_count[c]Increment 'have' when a required character's frequency matches the requirement.
while have == needTry to shrink the window from the left while it still contains all required characters.
window_count[s[left]] -= 1Remove the leftmost character from the window count as we shrink.
if s[left] in t_count and window_count[s[left]] < t_count[s[left]]Decrement 'have' if the window no longer satisfies the frequency requirement for a character.
import java.util.*;
public class MinWindowSubstringSliding {
    public static String minWindow(String s, String t) {
        if (s == null || t == null || s.length() == 0 || t.length() == 0) return "";
        Map<Character, Integer> tCount = new HashMap<>();
        for (char c : t.toCharArray()) tCount.put(c, tCount.getOrDefault(c, 0) + 1);
        Map<Character, Integer> windowCount = new HashMap<>();
        int have = 0, need = tCount.size();
        int left = 0, minLen = Integer.MAX_VALUE;
        int[] res = {-1, -1};
        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            windowCount.put(c, windowCount.getOrDefault(c, 0) + 1);
            if (tCount.containsKey(c) && windowCount.get(c).intValue() == tCount.get(c).intValue()) {
                have++;
            }
            while (have == need) {
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    res[0] = left;
                    res[1] = right;
                }
                char leftChar = s.charAt(left);
                windowCount.put(leftChar, windowCount.get(leftChar) - 1);
                if (tCount.containsKey(leftChar) && windowCount.get(leftChar) < tCount.get(leftChar)) {
                    have--;
                }
                left++;
            }
        }
        return res[0] == -1 ? "" : s.substring(res[0], res[1] + 1);
    }
    public static void main(String[] args) {
        System.out.println(minWindow("ADOBECODEBANC", "ABC")); // Output: BANC
    }
}
Line Notes
Map<Character, Integer> tCount = new HashMap<>();Count required characters in t to know frequency requirements.
Map<Character, Integer> windowCount = new HashMap<>();Track characters in the current window dynamically.
int have = 0, need = tCount.size();Track how many unique required characters are currently satisfied.
for (int right = 0; right < s.length(); right++)Expand window by moving right pointer through s.
if (tCount.containsKey(c) && windowCount.get(c).intValue() == tCount.get(c).intValue())Increment 'have' when a required character's frequency matches the requirement.
while (have == need)Shrink window from left while it still contains all required characters.
windowCount.put(leftChar, windowCount.get(leftChar) - 1);Remove left character from window count as window shrinks.
if (tCount.containsKey(leftChar) && windowCount.get(leftChar) < tCount.get(leftChar))Decrement 'have' if window no longer satisfies frequency requirement.
#include <iostream>
#include <string>
#include <unordered_map>
#include <climits>
using namespace std;

string minWindowSliding(string s, string t) {
    if (s.empty() || t.empty()) return "";
    unordered_map<char, int> tCount;
    for (char c : t) tCount[c]++;
    unordered_map<char, int> windowCount;
    int have = 0, need = (int)tCount.size();
    int left = 0, minLen = INT_MAX;
    pair<int, int> res = {-1, -1};
    for (int right = 0; right < (int)s.size(); right++) {
        char c = s[right];
        windowCount[c]++;
        if (tCount.count(c) && windowCount[c] == tCount[c]) {
            have++;
        }
        while (have == need) {
            if (right - left + 1 < minLen) {
                minLen = right - left + 1;
                res = {left, right};
            }
            char leftChar = s[left];
            windowCount[leftChar]--;
            if (tCount.count(leftChar) && windowCount[leftChar] < tCount[leftChar]) {
                have--;
            }
            left++;
        }
    }
    if (res.first == -1) return "";
    return s.substr(res.first, minLen);
}

int main() {
    cout << minWindowSliding("ADOBECODEBANC", "ABC") << endl; // Output: BANC
    return 0;
}
Line Notes
unordered_map<char, int> tCount;Count required characters in t to know frequency requirements.
unordered_map<char, int> windowCount;Track characters in the current window dynamically.
int have = 0, need = (int)tCount.size();Track how many unique required characters are currently satisfied.
for (int right = 0; right < (int)s.size(); right++)Expand window by moving right pointer through s.
if (tCount.count(c) && windowCount[c] == tCount[c])Increment 'have' when a required character's frequency matches the requirement.
while (have == need)Shrink window from left while it still contains all required characters.
windowCount[leftChar]--;Remove left character from window count as window shrinks.
if (tCount.count(leftChar) && windowCount[leftChar] < tCount[leftChar])Decrement 'have' if window no longer satisfies frequency requirement.
function minWindowSlidingWindow(s, t) {
    if (!s || !t) return "";
    const tCount = {};
    for (const c of t) tCount[c] = (tCount[c] || 0) + 1;
    const windowCount = {};
    let have = 0, need = Object.keys(tCount).length;
    let left = 0, minLen = Infinity;
    let res = [-1, -1];
    for (let right = 0; right < s.length; right++) {
        const c = s[right];
        windowCount[c] = (windowCount[c] || 0) + 1;
        if (tCount[c] && windowCount[c] === tCount[c]) {
            have++;
        }
        while (have === need) {
            if (right - left + 1 < minLen) {
                minLen = right - left + 1;
                res = [left, right];
            }
            const leftChar = s[left];
            windowCount[leftChar]--;
            if (tCount[leftChar] && windowCount[leftChar] < tCount[leftChar]) {
                have--;
            }
            left++;
        }
    }
    return res[0] === -1 ? "" : s.substring(res[0], res[1] + 1);
}

// Example usage:
console.log(minWindowSlidingWindow("ADOBECODEBANC", "ABC")); // Output: BANC
Line Notes
const tCount = {};Count required characters in t to know frequency requirements.
const windowCount = {};Track characters in the current window dynamically.
let have = 0, need = Object.keys(tCount).length;Track how many unique required characters are currently satisfied.
for (let right = 0; right < s.length; right++)Expand window by moving right pointer through s.
if (tCount[c] && windowCount[c] === tCount[c])Increment 'have' when a required character's frequency matches the requirement.
while (have === need)Shrink window from left while it still contains all required characters.
windowCount[leftChar]--;Remove left character from window count as window shrinks.
if (tCount[leftChar] && windowCount[leftChar] < tCount[leftChar])Decrement 'have' if window no longer satisfies frequency requirement.
⏱
Complexity
TimeO(n)
SpaceO(m)

Each character is visited at most twice by left and right pointers, and frequency maps use O(m) space where m is size of t.

💡 For n=10^5, this approach is efficient enough to run within time limits.
Interview Verdict: Accepted

This approach is optimal for this problem and is what interviewers expect for an efficient solution.

🧠
Optimized Sliding Window with Fixed-Size Frequency Arrays
💡 Using fixed-size arrays instead of hash maps can speed up frequency tracking when input is limited to ASCII letters.

Intuition

Replace hash maps with arrays indexed by character codes to reduce overhead and improve runtime.

Algorithm

  1. Initialize frequency arrays for t and the current window.
  2. Use two pointers to expand and shrink the window as in the sliding window approach.
  3. Update counts using array indices instead of hash maps.
  4. Track when the window satisfies all character requirements.
  5. Return the minimum window substring found.
💡 The logic is the same as the sliding window approach but uses arrays for faster frequency updates.
</>
Code
def min_window_optimized(s: str, t: str) -> str:
    if not s or not t:
        return ""
    from collections import defaultdict
    t_count = [0] * 128
    window_count = [0] * 128
    for c in t:
        t_count[ord(c)] += 1
    have, need = 0, sum(1 for x in t_count if x > 0)
    res, res_len = [-1, -1], float('inf')
    left = 0
    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
        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
    left, right = res
    return s[left:right+1] if res_len != float('inf') else ""

# Example usage:
if __name__ == "__main__":
    print(min_window_optimized("ADOBECODEBANC", "ABC"))  # Output: BANC
Line Notes
t_count = [0] * 128Initialize fixed-size array for ASCII character counts to enable O(1) access.
window_count = [0] * 128Track frequencies in current window using array for faster updates.
for c in t: t_count[ord(c)] += 1Count characters in t by ASCII index for direct access.
have, need = 0, sum(1 for x in t_count if x > 0)Count how many unique characters are needed to satisfy the window.
window_count[ord(c)] += 1Increment window count for current character efficiently.
if t_count[ord(c)] > 0 and window_count[ord(c)] == t_count[ord(c)]Check if requirement for character met to update 'have'.
while have == needShrink window while it remains valid to find minimum substring.
window_count[ord(s[left])] -= 1Remove left character from window count as window shrinks.
public class MinWindowSubstringOptimized {
    public static String minWindow(String s, String t) {
        if (s == null || t == null || s.length() == 0 || t.length() == 0) return "";
        int[] tCount = new int[128];
        int[] windowCount = new int[128];
        for (char c : t.toCharArray()) tCount[c]++;
        int have = 0, need = 0;
        for (int count : tCount) if (count > 0) need++;
        int left = 0, minLen = Integer.MAX_VALUE;
        int[] res = {-1, -1};
        for (int right = 0; right < s.length(); right++) {
            char c = s.charAt(right);
            windowCount[c]++;
            if (tCount[c] > 0 && windowCount[c] == tCount[c]) have++;
            while (have == need) {
                if (right - left + 1 < minLen) {
                    minLen = right - left + 1;
                    res[0] = left;
                    res[1] = right;
                }
                char leftChar = s.charAt(left);
                windowCount[leftChar]--;
                if (tCount[leftChar] > 0 && windowCount[leftChar] < tCount[leftChar]) have--;
                left++;
            }
        }
        return res[0] == -1 ? "" : s.substring(res[0], res[1] + 1);
    }
    public static void main(String[] args) {
        System.out.println(minWindow("ADOBECODEBANC", "ABC")); // Output: BANC
    }
}
Line Notes
int[] tCount = new int[128];Use fixed-size array for ASCII counts to enable constant-time access.
int[] windowCount = new int[128];Track window frequencies with array for efficient updates.
for (char c : t.toCharArray()) tCount[c]++;Count characters in t by ASCII index.
int have = 0, need = 0; for (int count : tCount) if (count > 0) need++;Count unique characters needed to satisfy window.
windowCount[c]++;Increment window count for current character.
if (tCount[c] > 0 && windowCount[c] == tCount[c]) have++;Increment 'have' when requirement met for a character.
while (have == need)Shrink window while it remains valid to find minimum substring.
windowCount[leftChar]--;Remove left character from window count as window shrinks.
#include <iostream>
#include <string>
#include <climits>
using namespace std;

string minWindowOptimized(string s, string t) {
    if (s.empty() || t.empty()) return "";
    int tCount[128] = {0};
    int windowCount[128] = {0};
    for (char c : t) tCount[c]++;
    int have = 0, need = 0;
    for (int i = 0; i < 128; i++) if (tCount[i] > 0) need++;
    int left = 0, minLen = INT_MAX;
    pair<int, int> res = {-1, -1};
    for (int right = 0; right < (int)s.size(); right++) {
        char c = s[right];
        windowCount[c]++;
        if (tCount[c] > 0 && windowCount[c] == tCount[c]) have++;
        while (have == need) {
            if (right - left + 1 < minLen) {
                minLen = right - left + 1;
                res = {left, right};
            }
            char leftChar = s[left];
            windowCount[leftChar]--;
            if (tCount[leftChar] > 0 && windowCount[leftChar] < tCount[leftChar]) have--;
            left++;
        }
    }
    if (res.first == -1) return "";
    return s.substr(res.first, minLen);
}

int main() {
    cout << minWindowOptimized("ADOBECODEBANC", "ABC") << endl; // Output: BANC
    return 0;
}
Line Notes
int tCount[128] = {0};Initialize fixed-size array for ASCII counts for constant-time access.
int windowCount[128] = {0};Track window frequencies with array for efficient updates.
for (char c : t) tCount[c]++;Count characters in t by ASCII index.
int have = 0, need = 0; for (int i = 0; i < 128; i++) if (tCount[i] > 0) need++;Count unique characters needed to satisfy window.
windowCount[c]++;Increment window count for current character.
if (tCount[c] > 0 && windowCount[c] == tCount[c]) have++;Increment 'have' when requirement met for a character.
while (have == need)Shrink window while it remains valid to find minimum substring.
windowCount[leftChar]--;Remove left character from window count as window shrinks.
function minWindowOptimized(s, t) {
    if (!s || !t) return "";
    const tCount = new Array(128).fill(0);
    const windowCount = new Array(128).fill(0);
    for (const c of t) tCount[c.charCodeAt(0)]++;
    let have = 0, need = tCount.filter(x => x > 0).length;
    let left = 0, minLen = Infinity;
    let res = [-1, -1];
    for (let right = 0; right < s.length; right++) {
        const c = s.charCodeAt(right);
        windowCount[c]++;
        if (tCount[c] > 0 && windowCount[c] === tCount[c]) have++;
        while (have === need) {
            if (right - left + 1 < minLen) {
                minLen = right - left + 1;
                res = [left, right];
            }
            const leftChar = s.charCodeAt(left);
            windowCount[leftChar]--;
            if (tCount[leftChar] > 0 && windowCount[leftChar] < tCount[leftChar]) have--;
            left++;
        }
    }
    return res[0] === -1 ? "" : s.substring(res[0], res[1] + 1);
}

// Example usage:
console.log(minWindowOptimized("ADOBECODEBANC", "ABC")); // Output: BANC
Line Notes
const tCount = new Array(128).fill(0);Initialize fixed-size array for ASCII counts for constant-time access.
const windowCount = new Array(128).fill(0);Track window frequencies with array for efficient updates.
for (const c of t) tCount[c.charCodeAt(0)]++;Count characters in t by ASCII index.
let have = 0, need = tCount.filter(x => x > 0).length;Count unique characters needed to satisfy window.
windowCount[c]++;Increment window count for current character.
if (tCount[c] > 0 && windowCount[c] === tCount[c]) have++;Increment 'have' when requirement met for a character.
while (have === need)Shrink window while it remains valid to find minimum substring.
windowCount[leftChar]--;Remove left character from window count as window shrinks.
⏱
Complexity
TimeO(n)
SpaceO(1)

Frequency arrays use constant space (128 ASCII chars), and two pointers traverse s once.

💡 This approach is the fastest in practice due to constant-time frequency lookups.
Interview Verdict: Accepted

This is a micro-optimization over the standard sliding window and is ideal when input is ASCII.

📊
All Approaches - One-Glance Tradeoffs
💡 In interviews, coding the sliding window approach (Approach 2) is usually best; brute force is for explanation only.
ApproachTimeSpaceStack RiskReconstructUse In Interview
1. Brute ForceO(n^3)O(n)NoN/AMention only - never code
2. Sliding Window with Hash MapsO(n)O(m)NoYesCode this approach in most interviews
3. Optimized Sliding Window with ArraysO(n)O(1)NoYesMention as optimization if input is ASCII
💼
Interview Strategy
💡 Use this guide to understand the problem deeply, practice coding all approaches, and prepare to explain tradeoffs clearly in interviews.

How to Present

Clarify the problem and constraints with the interviewer.Explain the brute force approach to show understanding of the problem.Introduce the sliding window technique as an optimization.Discuss further optimizations like fixed-size arrays if applicable.Write clean, tested code and explain your logic clearly.

Time Allocation

Clarify: 2min → Approach: 3min → Code: 10min → Test: 5min. Total ~20min

What the Interviewer Tests

The interviewer tests your ability to identify the sliding window pattern, manage frequency counts correctly, and optimize for time and space.

Common Follow-ups

  • What if t contains unicode characters? → Use hash maps instead of fixed arrays.
  • How to handle case-insensitive matching? → Normalize strings before processing.
💡 These follow-ups test your ability to adapt the solution to real-world variations and constraints.
🔍
Pattern Recognition

When to Use

1) Need to find substring containing all characters of another string, 2) Order of characters does not matter, 3) Need minimum or maximum length substring, 4) Characters have frequency requirements.

Signature Phrases

minimum window substringcontains all charactersfrequency of characters

NOT This Pattern When

Problems requiring subsequence matching or order-sensitive substring matching are different patterns.

Similar Problems

Substring with Concatenation of All Words - similar frequency and substring matchingLongest Substring Without Repeating Characters - sliding window patternFind All Anagrams in a String - frequency tracking with sliding window

Practice

(1/5)
1. Consider the following code snippet for computing the maximum path sum in a binary tree. Given the tree with root value 1, left child 2, and right child 3, what is the final returned maximum path sum?
easy
A. 3
B. 6
C. 5
D. 4

Solution

  1. Step 1: Trace helper on left child (2):

    left and right children are null, so left=0, right=0, max_sum updated to max(-inf, 2+0+0)=2, returns 2+max(0,0)=2.
  2. Step 2: Trace helper on right child (3):

    Similarly, max_sum updated to max(2, 3+0+0)=3, returns 3+max(0,0)=3.
  3. Step 3: Trace helper on root (1):

    left=2, right=3, max_sum updated to max(3, 1+2+3)=6, returns 1+max(2,3)=4.
  4. Final Answer:

    Option B -> Option B
  5. Quick Check:

    Max path is 2 -> 1 -> 3 with sum 6 [OK]
Hint: Sum includes node plus max gains from both children [OK]
Common Mistakes:
  • Returning sum of one child only
  • Off-by-one in max_sum update
  • Ignoring zero cutoff for negative gains
2. Given the following code snippet for MedianFinder, after adding the numbers 1, 5, and 3 in that order, what is the value returned by findMedian()?
easy
A. 4.0
B. 5.0
C. 1.0
D. 3.0

Solution

  1. Step 1: Insert 1

    small is empty, so push -1 to small. small_size=1, large_size=0.
  2. Step 2: Insert 5

    5 > -small[0] (which is 1), so push 5 to large. small_size=1, large_size=1. Heaps balanced.
  3. Step 3: Insert 3

    3 <= -small[0] (1)? No, so push 3 to large. large_size=2, small_size=1. Now large_size > small_size, so move smallest from large (3) to small (push -3). small_size=2, large_size=1.
  4. Step 4: Find median

    small_size > large_size, median is -small[0] = 3.0.
  5. Final Answer:

    Option D -> Option D
  6. Quick Check:

    Median after [1,5,3] is 3.0 [OK]
Hint: Median is top of larger heap when sizes differ [OK]
Common Mistakes:
  • Not balancing heaps after insertion
  • Returning average when sizes differ
  • Forgetting to negate values in max heap
3. What is the time complexity of the optimized approach that uses sorting, offline queries, and a bitwise trie for the Maximum XOR With Element From Array problem, given n elements in nums and q queries, assuming 32-bit integers?
medium
A. O((n + q) * log(maxNum))
B. O((n + q) * 32)
C. O(n * log(maxNum) * q)
D. O(n * q)

Solution

  1. Step 1: Analyze sorting and offline queries

    Sorting nums and queries takes O(n log n + q log q), dominated by O((n + q) log (n + q)) which is less than main complexity.
  2. Step 2: Analyze trie operations

    Each insertion and query takes O(log(maxNum)) = O(32) for 32-bit integers. Total insertions = n, total queries = q.
  3. Final Answer:

    Option A -> Option A
  4. Quick Check:

    Combined complexity is O((n + q) * log(maxNum)) [OK]
Hint: Trie ops per element and query scale with log(maxNum) [OK]
Common Mistakes:
  • Confusing n*q brute force with optimized approach
  • Ignoring log(maxNum) factor
  • Assuming linear time per query
4. What is the time complexity of the brute force approach for the trapping rain water problem, where for each bar you find the maximum height to the left and right using nested loops?
medium
A. O(n log n)
B. O(n)
C. O(n^2)
D. O(n^3)

Solution

  1. Step 1: Identify loops

    Outer loop runs n times; for each i, two inner loops run up to i and n-i times respectively.
  2. Step 2: Calculate total operations

    Each inner loop can run up to O(n), so total is O(n) * O(n) = O(n²).
  3. Final Answer:

    Option C -> Option C
  4. Quick Check:

    Nested loops over n elements -> quadratic time [OK]
Hint: Nested loops over n elements -> O(n²) time [OK]
Common Mistakes:
  • Confusing with prefix/suffix approach O(n)
  • Assuming sorting or log factor involved
  • Mistaking inner loops as constant time
5. Suppose the problem is modified so that digits can be reused any number of times to form expressions (e.g., "1+1+2+3" from "123"). Which modification to the backtracking algorithm correctly handles this variant without infinite loops?
hard
A. Remove the index increment in recursive calls to allow reuse of digits indefinitely
B. Add a visited set to track used indices and prevent reusing digits more than once
C. Allow recursive calls to reuse digits by not incrementing index, but limit expression length to avoid infinite recursion
D. Modify recursion to restart from index 0 after each operator insertion, but track expression length to prevent infinite loops

Solution

  1. Step 1: Understand reuse implications

    Allowing reuse means digits can be chosen multiple times, so index must be reset or not incremented to allow reuse.
  2. Step 2: Prevent infinite recursion

    Resetting index to 0 after operator insertion allows reuse, but expression length must be tracked and limited to avoid infinite loops.
  3. Step 3: Evaluate options

    Modify recursion to restart from index 0 after each operator insertion, but track expression length to prevent infinite loops correctly resets index and tracks length. Remove the index increment in recursive calls to allow reuse of digits indefinitely causes infinite loops by never incrementing index. Add a visited set to track used indices and prevent reusing digits more than once forbids reuse. Allow recursive calls to reuse digits by not incrementing index, but limit expression length to avoid infinite recursion risks infinite recursion without explicit length limit.
  4. Final Answer:

    Option D -> Option D
  5. Quick Check:

    Reset index with length limit prevents infinite recursion while allowing reuse [OK]
Hint: Reuse requires index reset plus recursion depth control [OK]
Common Mistakes:
  • Not limiting recursion depth causing infinite loops
  • Using visited set forbidding reuse
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard