Find the shortest window that contains every required item.
The counts map holds the values from left to right, missing counts the required copies they lack, and the shrink reaches the shortest cover ending at right.
Use it when the question asks for the shortest stretch that contains every required value, or each value at least a given number of times. Signal words: shortest, smallest, contains every, covers all, at least once, at least as many times.
State: a need map of the required counts, a counts map for the window, and the missing counter, which starts at the number of required copies.
Grow: the value at right joins. If its count is now at or below its need, missing goes down by 1.
Shrink and record: while missing is 0, record the window if it is strictly shorter, then the value at left leaves. If its count is now below its need, missing goes up by 1.
No cover: if missing never reaches 0, return a sentinel such as [-1, -1].
Cost: each value joins once and leaves at most once, so the pass is O(n + m).
def min_cover(values, required):
# [1] Count what is required; the empty window lacks every copy.
need = {}
for value in required:
need[value] = need.get(value, 0) + 1
missing = len(required)
window = {}
left = 0
best_start = -1
best_end = -1
for right in range(len(values)):
# [2] The value at right joins; missing drops only while its count is at or below its need.
joining = values[right]
window[joining] = window.get(joining, 0) + 1
if window[joining] <= need.get(joining, 0):
missing -= 1 # it fills a gap; a surplus copy fills none
# [3] While the window covers, record it if shorter, then the value at left leaves.
while missing == 0:
if best_start == -1 or right - left < best_end - best_start:
best_start = left # strictly shorter only, so the earliest of equal windows stays
best_end = right
leaving = values[left]
window[leaving] -= 1
if window[leaving] < need.get(leaving, 0):
missing += 1 # the window now lacks a copy it needs
left += 1
# [4] Report the shortest covering window, or [-1, -1] if none covers.
return [best_start, best_end]public int[] minCover(int[] values, int[] required) {
// [1] Count what is required; the empty window lacks every copy.
Map<Integer, Integer> need = new HashMap<>();
for (int value : required) {
need.put(value, need.getOrDefault(value, 0) + 1);
}
int missing = required.length;
Map<Integer, Integer> window = new HashMap<>();
int left = 0;
int bestStart = -1;
int bestEnd = -1;
for (int right = 0; right < values.length; right++) {
// [2] The value at right joins; missing drops only while its count is at or below its need.
int joining = values[right];
int count = window.getOrDefault(joining, 0) + 1;
window.put(joining, count);
if (count <= need.getOrDefault(joining, 0)) {
missing--; // it fills a gap; a surplus copy fills none
}
// [3] While the window covers, record it if shorter, then the value at left leaves.
while (missing == 0) {
if (bestStart == -1 || right - left < bestEnd - bestStart) {
bestStart = left; // strictly shorter only, so the earliest of equal windows stays
bestEnd = right;
}
int leaving = values[left];
int remaining = window.get(leaving) - 1;
window.put(leaving, remaining);
if (remaining < need.getOrDefault(leaving, 0)) {
missing++; // the window now lacks a copy it needs
}
left++;
}
}
// [4] Report the shortest covering window, or [-1, -1] if none covers.
return new int[] {bestStart, bestEnd};
}vector<int> minCover(const vector<int>& values, const vector<int>& required) {
// [1] Count what is required; the empty window lacks every copy.
unordered_map<int, int> need;
for (int value : required) {
need[value]++;
}
int missing = required.size();
unordered_map<int, int> window;
int left = 0;
int bestStart = -1;
int bestEnd = -1;
for (int right = 0; right < (int) values.size(); right++) {
// [2] The value at right joins; missing drops only while its count is at or below its need.
int joining = values[right];
int count = ++window[joining];
auto found = need.find(joining);
if (found != need.end() && count <= found->second) {
missing--; // it fills a gap; a surplus copy fills none
}
// [3] While the window covers, record it if shorter, then the value at left leaves.
while (missing == 0) {
if (bestStart == -1 || right - left < bestEnd - bestStart) {
bestStart = left; // strictly shorter only, so the earliest of equal windows stays
bestEnd = right;
}
int leaving = values[left];
int remaining = --window[leaving];
auto needed = need.find(leaving);
if (needed != need.end() && remaining < needed->second) {
missing++; // the window now lacks a copy it needs
}
left++;
}
}
// [4] Report the shortest covering window, or [-1, -1] if none covers.
return {bestStart, bestEnd};
}function minCover(values, required) {
// [1] Count what is required; the empty window lacks every copy.
const need = new Map();
for (const value of required) {
need.set(value, (need.get(value) || 0) + 1);
}
let missing = required.length;
const window = new Map();
let left = 0;
let bestStart = -1;
let bestEnd = -1;
for (let right = 0; right < values.length; right++) {
// [2] The value at right joins; missing drops only while its count is at or below its need.
const joining = values[right];
const count = (window.get(joining) || 0) + 1;
window.set(joining, count);
if (count <= (need.get(joining) || 0)) {
missing--; // it fills a gap; a surplus copy fills none
}
// [3] While the window covers, record it if shorter, then the value at left leaves.
while (missing === 0) {
if (bestStart === -1 || right - left < bestEnd - bestStart) {
bestStart = left; // strictly shorter only, so the earliest of equal windows stays
bestEnd = right;
}
const leaving = values[left];
const remaining = window.get(leaving) - 1;
window.set(leaving, remaining);
if (remaining < (need.get(leaving) || 0)) {
missing++; // the window now lacks a copy it needs
}
left++;
}
}
// [4] Report the shortest covering window, or [-1, -1] if none covers.
return [bestStart, bestEnd];
}if joining in need:
missing -= 1 # bug: a surplus copy fills no gapshrank = False
while missing == 0:
... # the value at left leaves, as in the template
shrank = True
if shrank and (best_start == -1 or right - left < best_end - best_start):
best_start, best_end = left, right # bug: the window has just lost a required copyif missing == 0: # bug: this must be a while loop
... # record, then the value at left leaveswhile all(window.get(value, 0) >= copies for value, copies in need.items()):
... # record, then the value at left leavesYour team lead brings a request from one sponsor: its tracks must play in order, the jingle first, then track 7, then track 2. Other tracks may sit between them. For the schedule [7, 5, 2, 8, 5, 7, 3, 2], the shortest such block is plays 4 to 7: 5, 7, 3, 2.
systemdesignmastery.dev/dsa/cheatsheets/minimum-cover