🧠
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
- Count the frequency of characters in t.
- Use two pointers (left and right) to represent the current window in s.
- Expand right pointer to include characters until the window contains all characters of t.
- Shrink left pointer to remove unnecessary characters and update minimum window.
- 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.
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.
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.