Mutating counts without cleanup
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Break down a hard problem into reliable checkpoints, edge-case handling, and complexity trade-offs.
Given two strings s and t of lengths m and n respectively, 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 "".
The testcases will be generated such that the answer is unique.
Example 1:
Input: s = "ADOBECODEBANC", t = "ABC" Output: "BANC" Explanation: The minimum window substring "BANC" includes 'A', 'B', and 'C' from string t.
Example 2:
Input: s = "a", t = "a" Output: "a" Explanation: The entire string s is the minimum window.
Example 3:
Input: s = "a", t = "aa" Output: "" Explanation: Both 'a's from t must be included in the window. Since the largest window of s only has one 'a', return empty string.
Constraints:
m == s.lengthn == t.length1 <= m, n <= 105s and t consist of uppercase and lowercase English letters.Follow up: Could you find an algorithm that runs in O(m + n) time?
Problem summary: Given two strings s and t of lengths m and n respectively, 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 "". The testcases will be generated such that the answer is unique.
Start with the most direct exhaustive search. That gives a correctness anchor before optimizing.
Pattern signal: Hash Map · Sliding Window
"ADOBECODEBANC" "ABC"
"a" "a"
"a" "aa"
substring-with-concatenation-of-all-words)minimum-size-subarray-sum)sliding-window-maximum)permutation-in-string)smallest-range-covering-elements-from-k-lists)class Solution {
public String minWindow(String s, String t) {
if (t.length() > s.length()) return "";
int[] need = new int[128];
for (char ch : t.toCharArray()) need[ch]++;
int required = t.length();
int left = 0;
int bestLen = Integer.MAX_VALUE, bestStart = 0;
for (int right = 0; right < s.length(); right++) {
char rc = s.charAt(right);
if (need[rc] > 0) required--;
need[rc]--;
while (required == 0) {
if (right - left + 1 < bestLen) {
bestLen = right - left + 1;
bestStart = left;
}
char lc = s.charAt(left++);
need[lc]++;
if (need[lc] > 0) required++;
}
}
return bestLen == Integer.MAX_VALUE ? "" : s.substring(bestStart, bestStart + bestLen);
}
}
func minWindow(s string, t string) string {
if len(t) > len(s) {
return ""
}
need := [128]int{}
for i := 0; i < len(t); i++ {
need[t[i]]++
}
required := len(t)
left := 0
bestLen := len(s) + 1
bestStart := 0
for right := 0; right < len(s); right++ {
rc := s[right]
if need[rc] > 0 {
required--
}
need[rc]--
for required == 0 {
if right-left+1 < bestLen {
bestLen = right - left + 1
bestStart = left
}
lc := s[left]
left++
need[lc]++
if need[lc] > 0 {
required++
}
}
}
if bestLen == len(s)+1 {
return ""
}
return s[bestStart : bestStart+bestLen]
}
class Solution:
def minWindow(self, s: str, t: str) -> str:
if len(t) > len(s):
return ""
need = {}
for ch in t:
need[ch] = need.get(ch, 0) + 1
required = len(t)
left = 0
best_len = float('inf')
best_start = 0
for right, ch in enumerate(s):
if need.get(ch, 0) > 0:
required -= 1
need[ch] = need.get(ch, 0) - 1
while required == 0:
if right - left + 1 < best_len:
best_len = right - left + 1
best_start = left
left_ch = s[left]
left += 1
need[left_ch] = need.get(left_ch, 0) + 1
if need[left_ch] > 0:
required += 1
return "" if best_len == float('inf') else s[best_start:best_start + best_len]
impl Solution {
pub fn min_window(s: String, t: String) -> String {
if t.len() > s.len() {
return String::new();
}
let mut need = [0i32; 128];
for &b in t.as_bytes() {
need[b as usize] += 1;
}
let bytes = s.as_bytes();
let mut required = t.len() as i32;
let mut left = 0usize;
let mut best_len = usize::MAX;
let mut best_start = 0usize;
for right in 0..bytes.len() {
let rc = bytes[right] as usize;
if need[rc] > 0 {
required -= 1;
}
need[rc] -= 1;
while required == 0 {
if right - left + 1 < best_len {
best_len = right - left + 1;
best_start = left;
}
let lc = bytes[left] as usize;
left += 1;
need[lc] += 1;
if need[lc] > 0 {
required += 1;
}
}
}
if best_len == usize::MAX {
String::new()
} else {
s[best_start..best_start + best_len].to_string()
}
}
}
function minWindow(s: string, t: string): string {
if (t.length > s.length) return '';
const need: number[] = Array(128).fill(0);
for (const ch of t) need[ch.charCodeAt(0)]++;
let required = t.length;
let left = 0;
let bestLen = Infinity;
let bestStart = 0;
for (let right = 0; right < s.length; right++) {
const rc = s.charCodeAt(right);
if (need[rc] > 0) required--;
need[rc]--;
while (required === 0) {
if (right - left + 1 < bestLen) {
bestLen = right - left + 1;
bestStart = left;
}
const lc = s.charCodeAt(left++);
need[lc]++;
if (need[lc] > 0) required++;
}
}
return bestLen === Infinity ? '' : s.slice(bestStart, bestStart + bestLen);
}
Use this to step through a reusable interview workflow for this problem.
For each starting index, scan the next k elements to compute the window aggregate. There are n−k+1 starting positions, each requiring O(k) work, giving O(n × k) total. No extra space since we recompute from scratch each time.
The window expands and contracts as we scan left to right. Each element enters the window at most once and leaves at most once, giving 2n total operations = O(n). Space depends on what we track inside the window (a hash map of at most k distinct elements, or O(1) for a fixed-size window).
Review these before coding to avoid predictable interview regressions.
Wrong move: Zero-count keys stay in map and break distinct/count constraints.
Usually fails on: Window/map size checks are consistently off by one.
Fix: Delete keys when count reaches zero.
Wrong move: Using `if` instead of `while` leaves the window invalid for multiple iterations.
Usually fails on: Over-limit windows stay invalid and produce wrong lengths/counts.
Fix: Shrink in a `while` loop until the invariant is valid again.