Minimum Window Substring in DSA Javascript - Time & Space Complexity
We want to know how the time needed to find the smallest substring containing all characters grows as the input string gets longer.
How does the algorithm's work increase when the input size grows?
Analyze the time complexity of the following code snippet.
function minWindow(s, t) {
const need = new Map();
for (const c of t) need.set(c, (need.get(c) || 0) + 1);
let left = 0, right = 0, missing = t.length, start = 0, minLen = Infinity;
while (right < s.length) {
if (need.has(s[right])) {
if (need.get(s[right]) > 0) missing--;
need.set(s[right], need.get(s[right]) - 1);
}
right++;
while (missing === 0) {
if (right - left < minLen) {
start = left;
minLen = right - left;
}
if (need.has(s[left])) {
need.set(s[left], need.get(s[left]) + 1);
if (need.get(s[left]) > 0) missing++;
}
left++;
}
}
return minLen === Infinity ? "" : s.substring(start, start + minLen);
}
This code finds the smallest substring in s that contains all characters of t.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The
whileloop moving therightpointer through strings, and the innerwhileloop moving theleftpointer to shrink the window. - How many times: Each character in
sis visited at most twice, once whenrightmoves forward and once whenleftmoves forward.
As the input string s grows, the algorithm scans through it with two pointers that each move forward without going back.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 moves of pointers |
| 100 | About 200 moves of pointers |
| 1000 | About 2000 moves of pointers |
Pattern observation: The number of operations grows roughly twice the input size, so it grows linearly.
Time Complexity: O(n + m)
This means the time grows linearly with the size of both input strings s and t.
[X] Wrong: "The nested while loops make this algorithm quadratic, so it is O(n²)."
[OK] Correct: Each pointer only moves forward through s once, so total work is linear, not quadratic.
Understanding how two-pointer sliding window techniques work efficiently is a valuable skill for many string problems in interviews.
"What if we changed the algorithm to reset the left pointer back to zero each time we find a valid window? How would the time complexity change?"
Practice
s and t?Solution
Step 1: Understand the problem goal
The problem asks to find the smallest part of stringsthat has all characters from stringt, including duplicates.Step 2: Compare options with the problem statement
The smallest substring insthat contains all characters oftmatches this goal exactly. Other options describe different problems.Final Answer:
The smallest substring insthat contains all characters oft-> Option CQuick Check:
Minimum Window Substring = smallest substring with all chars [OK]
- Confusing longest substring with smallest
- Ignoring duplicate characters in
t - Thinking it counts occurrences instead of substring
t?Solution
Step 1: Check initialization and increment logic
const freq = {}; for (const char of t) { freq[char] = (freq[char] || 0) + 1; } initializes an empty object and increments counts safely usingfreq[char] = (freq[char] || 0) + 1, which works even iffreq[char]is undefined.Step 2: Identify errors in other options
const freq = new Map(); for (const char of t) { freq.set(char, freq.get(char) + 1); } tries to use Map but callsfreq.get(char) + 1without checking iffreq.get(char)is undefined, causing NaN. const freq = {}; for (let i = 0; i < t.length; i++) { freq[t[i]] = freq[t[i]] + 1; } tries to add 1 to undefined without default. const freq = []; for (const char of t) { freq[char] += 1; } uses an array but accesses with string keys, which is invalid.Final Answer:
const freq = {}; for (const char of t) { freq[char] = (freq[char] || 0) + 1; } -> Option AQuick Check:
Safe frequency map init = const freq = {}; for (const char of t) { freq[char] = (freq[char] || 0) + 1; } [OK]
- Not initializing counts before adding
- Using Map without checking undefined values
- Using array with string keys
const s = "ADOBECODEBANC";
const t = "ABC";
function minWindow(s, t) {
const need = {};
for (const c of t) need[c] = (need[c] || 0) + 1;
let left = 0, right = 0, count = t.length;
let minLen = Infinity, start = 0;
while (right < s.length) {
if (need[s[right]] > 0) count--;
need[s[right]] = (need[s[right]] || 0) - 1;
right++;
while (count === 0) {
if (right - left < minLen) {
minLen = right - left;
start = left;
}
need[s[left]]++;
if (need[s[left]] > 0) count++;
left++;
}
}
return minLen === Infinity ? "" : s.substring(start, start + minLen);
}
console.log(minWindow(s, t));Solution
Step 1: Understand the sliding window logic
The code uses two pointers expanding and shrinking a window overs. It tracks needed characters fromtand shrinks window when all are found.Step 2: Trace the window to find minimum substring
Fors = "ADOBECODEBANC"andt = "ABC", the smallest substring containing all chars is "BANC" starting at index 9 with length 4.Final Answer:
"BANC" -> Option DQuick Check:
Minimum window substring = "BANC" [OK]
- Confusing substring start and end indices
- Not updating minimum length correctly
- Ignoring character counts when shrinking window
function minWindow(s, t) {
const need = {};
for (const c of t) need[c] = (need[c] || 0) + 1;
let left = 0, right = 0, count = t.length;
let minLen = Infinity, start = 0;
while (right < s.length) {
if (need[s[right]] > 0) count--;
need[s[right]]--;
right++;
while (count === 0) {
if (right - left < minLen) {
minLen = right - left;
start = left;
}
need[s[left]]++;
if (need[s[left]] > 0) count++;
left++;
}
}
return minLen === Infinity ? "" : s.substring(start, start + minLen);
}Solution
Step 1: Check how need map is updated
The code doesneed[s[right]]--without ensuringneed[s[right]]is defined. Ifs[right]is not int,need[s[right]]is undefined, causing NaN on decrement.Step 2: Fix by defaulting undefined to 0 before decrement
Useneed[s[right]] = (need[s[right]] || 0) - 1to avoid NaN and keep counts correct.Final Answer:
The decrement of need[s[right]] should use (need[s[right]] || 0) - 1 to avoid undefined -> Option BQuick Check:
Safe decrement with default 0 avoids NaN [OK]
- Not handling characters not in
t - Wrong initialization of count variable
- Incorrect substring slicing
s = "aabbccdd" and t = "abc", what is the minimum window substring that contains all characters of t considering duplicates in t only once?Solution
Step 1: Understand character requirements from
Stringtt = "abc"has characters 'a', 'b', 'c' each once. So the window must contain at least one 'a', one 'b', and one 'c'.Step 2: Find smallest substring in
Inscontaining 'a', 'b', 'c's = "aabbccdd", the substring "abc" starting at index 1 (characters at indices 1:'a', 2:'b', 3:'b', 4:'c') is not contiguous as "abc" is not directly present, but the smallest substring containing all characters once is actually "abc" starting at index 1 or 2. However, the substring "abc" is present starting at index 1 to 3 ("abb") is missing 'c', so the smallest substring containing all is "abc" at indices 1 to 3 is invalid. The substring "abbc" (indices 1 to 4) contains all characters and is minimal. But the substring "abc" (indices 1 to 3) is missing 'c'. The substring "abc" (indices 2 to 4) is "bbc" missing 'a'. The substring "abc" (indices 0 to 2) is "aab" missing 'c'. The substring "abc" (indices 3 to 5) is "bcc" missing 'a'. The substring "abc" (indices 1 to 4) is "abbc" which contains all. The substring "abc" (indices 1 to 3) is "abb" missing 'c'. The substring "abc" (indices 2 to 4) is "bbc" missing 'a'. The substring "abc" (indices 0 to 4) is "aabbc" which is longer. The substring "abc" (indices 1 to 4) "abbc" is the smallest substring containing all characters once. However, the option "abc" is present as "abc", but it is not a substring of s. So the correct minimal substring is "abbc".Final Answer:
"abbc" -> Option AQuick Check:
Minimum window with all chars once = "abbc" [OK]
- Counting duplicates in t multiple times
- Choosing longer substrings unnecessarily
- Thinking it counts occurrences instead of substring
Ready to prove it?
Pass the test and this pattern moves unknown
