Minimum Window Substring in DSA Typescript - Time & Space Complexity
We want to understand how the time needed to find the smallest substring containing all characters from another string changes as input size grows.
How does the algorithm's work increase when the strings get longer?
Analyze the time complexity of the following code snippet.
function minWindow(s: string, t: string): string {
const need = new Map();
for (const c of t) need.set(c, (need.get(c) || 0) + 1);
let left = 0, right = 0, count = t.length;
let minLen = Infinity, start = 0;
while (right < s.length) {
if (need.has(s[right])) {
if (need.get(s[right])! > 0) count--;
need.set(s[right], need.get(s[right])! - 1);
}
right++;
while (count === 0) {
if (right - left < minLen) {
minLen = right - left;
start = left;
}
if (need.has(s[left])) {
need.set(s[left], need.get(s[left])! + 1);
if (need.get(s[left])! > 0) count++;
}
left++;
}
}
return minLen === Infinity ? "" : s.substring(start, start + minLen);
}
This code finds the smallest substring in s that contains all characters from t using a sliding window approach.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The outer
whileloop moves therightpointer through strings. - Secondary operation: The inner
whileloop moves theleftpointer to shrink the window when all needed characters are found. - How many times: Each character in
sis visited at most twice, once byrightand once byleft.
As the input string s grows, the pointers move through it linearly.
| Input Size (n = length of s) | Approx. Operations |
|---|---|
| 10 | About 20 pointer moves (each character visited twice) |
| 100 | About 200 pointer moves |
| 1000 | About 2000 pointer moves |
Pattern observation: The number of operations grows roughly twice the input size, which is still linear growth.
Time Complexity: O(n + m)
This means the time grows linearly with the size of both strings s and t.
[X] Wrong: "Because there are nested loops, the time complexity must be quadratic (O(n²))."
[OK] Correct: The inner loop doesn't run fully for each outer loop step; both pointers only move forward through s once, so total work is linear.
Understanding this sliding window approach and its linear time complexity shows you can handle efficient substring problems, a common skill interviewers look for.
"What if we used a nested loop that restarted the left pointer from zero each time? How would the time complexity change?"
Practice
Minimum Window Substring problem?Solution
Step 1: Understand the problem statement
The problem asks to find the smallest part of a string that has all characters from another string.Step 2: Identify the goal
The goal is to find the minimum length substring that contains all required characters, not just any substring.Final Answer:
Find the smallest substring in a string that contains all characters of another string -> Option BQuick Check:
Minimum window substring = smallest substring with all chars [OK]
- Confusing with longest substring problems
- Ignoring the requirement to include all characters
- Thinking it's about sorting characters
t for the Minimum Window Substring problem?Solution
Step 1: Check correct Map usage in TypeScript
const freqMap = new Map(); for (const ch of t) { freqMap.set(ch, (freqMap.get(ch) ?? 0) + 1); } uses Map with get and set methods and handles undefined with nullish coalescing (?? 0).Step 2: Identify errors in other options
const freqMap = {}; for (let i = 0; i < t.length; i++) { freqMap[t[i]] = freqMap[t[i]] + 1; } uses object but does not initialize counts, causing NaN. const freqMap = new Map(); for (const ch of t) { freqMap[ch] = freqMap[ch] + 1; } uses Map but accesses like object property, which is incorrect. const freqMap = new Map(); for (const ch of t) { freqMap.set(ch, freqMap.get(ch) + 1); } does not handle undefined before adding 1, causing NaN.Final Answer:
const freqMap = new Map(); for (const ch of t) { freqMap.set(ch, (freqMap.get(ch) ?? 0) + 1); } -> Option CQuick Check:
Use Map.get with ?? 0 to avoid undefined [OK]
- Accessing Map like an object property
- Not initializing counts before increment
- Using object without initializing keys
s = "ADOBECODEBANC" and t = "ABC"?
function minWindow(s: string, t: string): string {
const freqMap = new Map();
for (const ch of t) freqMap.set(ch, (freqMap.get(ch) ?? 0) + 1);
let left = 0, right = 0, count = t.length, minLen = Infinity, start = 0;
while (right < s.length) {
const ch = s[right];
if (freqMap.has(ch)) {
freqMap.set(ch, freqMap.get(ch)! - 1);
if (freqMap.get(ch)! >= 0) count--;
}
while (count === 0) {
if (right - left + 1 < minLen) {
minLen = right - left + 1;
start = left;
}
const leftChar = s[left];
if (freqMap.has(leftChar)) {
freqMap.set(leftChar, freqMap.get(leftChar)! + 1);
if (freqMap.get(leftChar)! > 0) count++;
}
left++;
}
right++;
}
return minLen === Infinity ? "" : s.substring(start, start + minLen);
}
console.log(minWindow(s, t));Solution
Step 1: Understand the sliding window approach
The code uses two pointers expanding and shrinking the window to find the smallest substring containing all chars of t.Step 2: Trace the code with given input
The minimum window substring containing A, B, and C in s = "ADOBECODEBANC" and t = "ABC" is "BANC".Final Answer:
"BANC" -> Option AQuick Check:
Minimum window substring for "ABC" in "ADOBECODEBANC" is "BANC" [OK]
- Returning first found substring instead of minimum
- Not updating minimum length correctly
- Incorrectly increasing count when shrinking window
function minWindow(s: string, t: string): string {
const freqMap = new Map();
for (const ch of t) freqMap.set(ch, (freqMap.get(ch) ?? 0) + 1);
let left = 0, right = 0, count = t.length, minLen = Infinity, start = 0;
while (right < s.length) {
const ch = s[right];
if (freqMap.has(ch)) {
freqMap.set(ch, freqMap.get(ch)! - 1);
if (freqMap.get(ch)! > 0) count--;
}
while (count === 0) {
if (right - left + 1 < minLen) {
minLen = right - left + 1;
start = left;
}
const leftChar = s[left];
if (freqMap.has(leftChar)) {
freqMap.set(leftChar, freqMap.get(leftChar)! + 1);
if (freqMap.get(leftChar)! > 0) count++;
}
left++;
}
right++;
}
return minLen === Infinity ? "" : s.substring(start, start + minLen);
}Solution
Step 1: Identify the condition for decreasing count
The count should decrease when a needed character is found and its frequency is still >= 0, not > 0.Step 2: Fix the condition
Changing from > 0 to >= 0 ensures count decreases correctly when the character frequency reaches zero.Final Answer:
Change condition from if (freqMap.get(ch)! > 0) count--; to if (freqMap.get(ch)! >= 0) count--; -> Option AQuick Check:
Use >= 0 to count chars correctly [OK]
- Using > 0 causes missing valid matches
- Incorrect count initialization
- Removing inner loop breaks shrinking window
s = "aabbccdd" and t = "abc", which of the following substrings is the correct minimum window substring containing all characters of t? Consider that characters can appear multiple times in s but only once in t.Solution
Step 1: Understand character requirements
String t requires one 'a', one 'b', and one 'c'. The substring must contain at least these once.Step 2: Find smallest substring in s containing 'a', 'b', 'c'
"abbc" contains 'a', 'b', 'b', 'c' and is length 4. "abc" does not appear as a contiguous substring in s. "bcca" and "aabbc" are longer substrings.Final Answer:
"abbc" -> Option DQuick Check:
Minimum window substring with all chars = "abbc" [OK]
- Assuming "abc" substring exists exactly
- Ignoring repeated characters in s
- Choosing longer substrings unnecessarily
Ready to prove it?
Pass the test and this pattern moves unknown
