Minimum Window Substring in DSA C# - Time & Space Complexity
We want to understand how the time needed to find the smallest substring containing all characters grows as the input string gets longer.
How does the algorithm's speed change when the input size increases?
Analyze the time complexity of the following code snippet.
public string MinWindow(string s, string t) {
var dictT = new Dictionary<char, int>();
foreach (var c in t) dictT[c] = dictT.GetValueOrDefault(c, 0) + 1;
int required = dictT.Count, formed = 0;
var windowCounts = new Dictionary<char, int>();
int l = 0, r = 0, minLen = int.MaxValue, minLeft = 0;
while (r < s.Length) {
char c = s[r];
windowCounts[c] = windowCounts.GetValueOrDefault(c, 0) + 1;
if (dictT.ContainsKey(c) && windowCounts[c] == dictT[c]) formed++;
while (l <= r && formed == required) {
if (r - l + 1 < minLen) {
minLen = r - l + 1;
minLeft = l;
}
char leftChar = s[l];
windowCounts[leftChar]--;
if (dictT.ContainsKey(leftChar) && windowCounts[leftChar] < dictT[leftChar]) formed--;
l++;
}
r++;
}
return minLen == int.MaxValue ? "" : s.Substring(minLeft, minLen);
}
This code finds the smallest substring in s that contains all characters of t using a sliding window approach.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: The outer while loop moves the right pointer
rthrough the strings. - Nested operation: The inner while loop moves the left pointer
lto shrink the window when conditions are met. - How many times: Each character in
sis visited at most twice, once whenrmoves forward and once whenlmoves forward.
As the input string s grows, the pointers move through it, but each character is processed a limited number of times.
| 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 total operations grow roughly twice the input size, so the growth is linear.
Time Complexity: O(n + m)
This means the time grows linearly with the size of the input string s and the target string t.
[X] Wrong: "Because there are nested loops, the time complexity must be quadratic O(n²)."
[OK] Correct: The inner loop does not run fully for each outer loop iteration; both pointers only move forward, 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 in coding interviews.
"What if we used a nested for-loop to check every substring instead of two pointers? 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 includes every character from stringtat least once.Step 2: Compare options with the problem goal
The smallest substring insthat contains all characters oftmatches the goal exactly. The longest substring without characters fromt, the count oftappearances, or exact equality totdescribe unrelated problems.Final Answer:
The smallest substring in s that contains all characters of t -> Option DQuick Check:
Minimum window substring = smallest substring containing all chars [OK]
- Confusing minimum window with longest substring
- Thinking it counts occurrences instead of covering characters
- Assuming substring must be equal to t
t for the Minimum Window Substring problem?Solution
Step 1: Check dictionary type and initialization
Dictionary<char, int>is correct for counting characters.Dictionary<int, char>uses wrong types. Adding keys withAddfails on duplicates int. Incrementing with+=on uninitialized keys causes exceptions.Step 2: Verify counting logic
GetValueOrDefault(c, 0) + 1safely handles missing keys and increments correctly for duplicates.Add(c, 1)throws on duplicates.dict[c] += 1throwsKeyNotFoundExceptionon first occurrence.Final Answer:
var dict = new Dictionary<char, int>(); foreach (var c in t) { dict[c] = dict.GetValueOrDefault(c, 0) + 1; } -> Option BQuick Check:
Correct dictionary initialization and counting = var dict = new Dictionary(); foreach (var c in t) { dict[c] = dict.GetValueOrDefault(c, 0) + 1; } [OK]
- Using wrong dictionary key/value types
- Adding duplicate keys without checking
- Incrementing dictionary values without initialization
string s = "ADOBECODEBANC";
string t = "ABC";
string result = MinWindow(s, t);
Console.WriteLine(result);
string MinWindow(string s, string t) {
var dictT = new Dictionary();
foreach (var c in t) dictT[c] = dictT.GetValueOrDefault(c, 0) + 1;
int required = dictT.Count;
int l = 0, r = 0, formed = 0;
var windowCounts = new Dictionary();
int[] ans = {-1, 0, 0};
while (r < s.Length) {
char c = s[r];
windowCounts[c] = windowCounts.GetValueOrDefault(c, 0) + 1;
if (dictT.ContainsKey(c) && windowCounts[c] == dictT[c]) formed++;
while (l <= r && formed == required) {
c = s[l];
if (ans[0] == -1 || r - l + 1 < ans[0]) {
ans[0] = r - l + 1;
ans[1] = l;
ans[2] = r;
}
windowCounts[c]--;
if (dictT.ContainsKey(c) && windowCounts[c] < dictT[c]) formed--;
l++;
}
r++;
}
return ans[0] == -1 ? "" : s.Substring(ans[1], ans[0]);
}Solution
Step 1: Understand the sliding window logic
The code uses two pointers to find the smallest substring inscontaining all chars oft. It expands right pointerrto include chars and moves left pointerlto minimize window while keeping all required chars.Step 2: Trace the example input
Fors = "ADOBECODEBANC"andt = "ABC", the minimum window substring containing 'A', 'B', and 'C' is "BANC" starting at index 9 with length 4.Final Answer:
"BANC" -> Option CQuick Check:
Minimum window substring for given input = "BANC" [OK]
- Returning first found substring instead of minimum
- Not updating minimum window when smaller found
- Incorrectly updating counts causing wrong formed count
string MinWindow(string s, string t) {
var dictT = new Dictionary();
foreach (var c in t) dictT[c] = dictT.GetValueOrDefault(c, 0) + 1;
int required = dictT.Count;
int l = 0, r = 0, formed = 0;
var windowCounts = new Dictionary();
int[] ans = {-1, 0, 0};
while (r < s.Length) {
char c = s[r];
windowCounts[c] = windowCounts.GetValueOrDefault(c, 0) + 1;
if (dictT.ContainsKey(c) && windowCounts[c] == dictT[c]) formed++;
while (l <= r && formed == required) {
c = s[l];
if (ans[0] == -1 || r - l + 1 < ans[0]) {
ans[0] = r - l + 1;
ans[1] = l;
ans[2] = r;
}
windowCounts[c]--;
if (dictT.ContainsKey(c) && windowCounts[c] <= dictT[c]) formed--;
l++;
}
r++;
}
return ans[0] == -1 ? "" : s.Substring(ans[1], ans[0]);
}Solution
Step 1: Analyze the condition for decreasing formed count
The bug is in the lineif (dictT.ContainsKey(c) && windowCounts[c] <= dictT[c]) formed--;. Using <= causes formed to decrease even when counts are equal, prematurely reducing formed.Step 2: Correct the condition to strictly less than
Changing condition towindowCounts[c] < dictT[c]ensures formed decreases only when count falls below required, fixing the bug.Final Answer:
Change condition from windowCounts[c] <= dictT[c] to windowCounts[c] < dictT[c] when decreasing formed -> Option AQuick Check:
Strict less than fixes formed decrement bug [OK]
- Using <= instead of < causing wrong formed count
- Incorrect initialization of formed variable
- Removing inner loop causing incorrect window shrinking
s = "aabbccdd" and t = "abc", what is the minimum window substring that contains all characters of t? Consider that characters can appear multiple times in s but only once in t.Solution
Step 1: Identify required characters and counts
Stringtrequires one 'a', one 'b', and one 'c'. We need the smallest substring inscontaining at least one of each.Step 2: Find minimum substring in
Checking substrings: "aabbcc" (positions 0 to 5) contains 'a', 'b', and 'c' at least once each and is the smallest substring covering all required characters. "abbc" (positions 1 to 4) misses 'c' at position 4 (which is 'b'), so it does not contain 'c'. "abc" does not appear consecutively. "bcca" is not a substring in order. So "aabbcc" is the smallest valid window.scovering all required charsFinal Answer:
"aabbcc" -> Option AQuick Check:
Minimum window substring with all chars = "aabbcc" [OK]
- Choosing substring missing one required character
- Picking substring with correct chars but longer length
- Ignoring character counts in t
Ready to prove it?
Pass the test and this pattern moves unknown
