Plain Stack
Use when the newest unresolved item must be handled first: brackets, decoders, paths, and nested state.
Coding Interviews
A coding chapter for high-frequency Blind 75 patterns that show up in parsers, streaming telemetry, queue monitors, and speech serving incident tools.
Pattern Method
These problems are rarely hard because of syntax. They are hard because candidates lose track of what a stack or window guarantees after each update.
Use when the newest unresolved item must be handled first: brackets, decoders, paths, and nested state.
Use when each item waits for the next greater, next smaller, or boundary-breaking value.
Use when a contiguous range can be repaired incrementally by moving one boundary.
State what the stack stores, what order it preserves, and when an element becomes resolved. For example: the stack stores indices whose temperatures have not yet found a warmer future day, and temperatures at those indices are decreasing from bottom to top.
Worked Drills
Try each prompt first. Then open the hidden answer and compare your invariant, edge cases, and complexity explanation.
Given a string containing bracket characters, return whether every opener is closed by the same bracket type in the correct order.
Invariant: the stack stores the exact closing brackets required by the processed prefix. Test empty string, one opener, one closer, mixed nesting, and leftover openers.
def is_valid_parentheses(s):
closing = {"(": ")", "[": "]", "{": "}"}
stack = []
for ch in s:
if ch in closing:
stack.append(closing[ch])
elif not stack or stack.pop() != ch:
return False
return not stack
Given daily temperatures, return how many days each position must wait until a warmer temperature. Use zero if no warmer day exists.
Invariant: stack indices are unresolved days, and their temperatures are decreasing. When the current temperature is warmer than the stack top, the current index resolves that older day.
def daily_temperatures(temperatures):
answer = [0] * len(temperatures)
stack = []
for i, temp in enumerate(temperatures):
while stack and temperatures[stack[-1]] < temp:
j = stack.pop()
answer[j] = i - j
stack.append(i)
return answer
Given bar heights, return the area of the largest rectangle that can be formed by contiguous bars.
Invariant: the stack stores increasing bar indices. A shorter bar ends rectangles for taller bars on the stack. Common mistakes are forgetting the sentinel zero and using the wrong width after pop.
def largest_rectangle_area(heights):
stack = []
best = 0
for i, height in enumerate(heights + [0]):
while stack and heights[stack[-1]] > height:
h = heights[stack.pop()]
left = stack[-1] if stack else -1
width = i - left - 1
best = max(best, h * width)
stack.append(i)
return best
Given a string and integer k, return the length of the
longest substring that can be made of one repeated character after at
most k replacements.
Invariant: if window length minus the most frequent character count
exceeds k, the window cannot be repaired and must
shrink. The stored max frequency may be stale; it still preserves a
correct best length because it only delays shrinking, never creates
an impossible recorded answer.
from collections import defaultdict
def character_replacement(s, k):
counts = defaultdict(int)
left = 0
best = 0
max_count = 0
for right, ch in enumerate(s):
counts[ch] += 1
max_count = max(max_count, counts[ch])
while right - left + 1 - max_count > k:
counts[s[left]] -= 1
left += 1
best = max(best, right - left + 1)
return best
Given strings s and t, return the minimum
window in s containing all characters of t
with multiplicity.
Invariant: formed == required means every required
character count is satisfied. Test repeated required characters,
absent targets, target longer than source, and a best window at the
beginning or end.
from collections import Counter, defaultdict
def min_window(s, t):
need = Counter(t)
window = defaultdict(int)
required = len(need)
formed = 0
best = (float("inf"), 0, 0)
left = 0
for right, ch in enumerate(s):
window[ch] += 1
if ch in need and window[ch] == need[ch]:
formed += 1
while formed == required:
if right - left + 1 < best[0]:
best = (right - left + 1, left, right)
drop = s[left]
window[drop] -= 1
if drop in need and window[drop] < need[drop]:
formed -= 1
left += 1
return "" if best[0] == float("inf") else s[best[1]:best[2] + 1]
Decode strings such as 3[a2[c]], where a number before
brackets repeats the bracketed substring.
Invariant: every stack frame stores the prefix before a bracket and the repeat count for that bracket. On close, the completed fragment is appended back to the previous prefix. Common mistakes are failing multi-digit counts and losing nested prefixes.
def decode_string(s):
stack = []
current = []
number = 0
for ch in s:
if ch.isdigit():
number = number * 10 + int(ch)
elif ch == "[":
stack.append(("".join(current), number))
current = []
number = 0
elif ch == "]":
prefix, repeat = stack.pop()
current = [prefix + "".join(current) * repeat]
else:
current.append(ch)
return "".join(current)
Production Follow-Ups
Given per-minute queue ages, return the longest window whose average
age exceeds an alert threshold after allowing at most
k noisy minutes to be ignored.
Clarify whether ignored minutes are removed from the denominator or
tolerated as exceptions. If the rule is exception-based, use a
sliding window over minutes whose age is above threshold and shrink
when exceptions exceed k. Connect the result to ASR or
TTS backlog alerts and rollout freeze decisions.
A voice agent emits partial JSON-like tool-call text. How would you detect whether the stream is balanced enough to hand to a validator?
Use a stack for brackets and braces, track string and escape state, and avoid executing tools from partial text. A strong answer adds a timeout, maximum nesting, schema validation, confirmation for sensitive actions, and logging that never stores raw private audio.
Exam Checks
In Longest Repeating Character Replacement, why can the window keep a stale maximum frequency instead of recomputing it after shrinking?
The algorithm records a maximum length only after expanding. A stale max can make the window appear valid longer than it truly is, but it cannot cause a too-large answer because the stale value came from a real earlier window with that frequency. It is a proof about preserving best length, not a claim that every intermediate window is valid.
You need to diagnose partial transcript churn over a stream. When is it a stack problem, and when is it a sliding-window problem?
Use a stack when nested structure or last-unresolved state matters, such as parsing partial tool calls or bracketed markup. Use a sliding window when the metric is over a contiguous time range, such as churn rate over the last five seconds or rollback alerts over the latest canary minutes.