Coding Interviews

Stacks, Monotonic Structures, And Sliding Windows

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

Name The Invariant Before The Code

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.

Plain Stack

Use when the newest unresolved item must be handled first: brackets, decoders, paths, and nested state.

Monotonic Stack

Use when each item waits for the next greater, next smaller, or boundary-breaking value.

Sliding Window

Use when a contiguous range can be repaired incrementally by moving one boundary.

Question: What should you say before coding a monotonic-stack solution?

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

Six Problems To Know Cold

Try each prompt first. Then open the hidden answer and compare your invariant, edge cases, and complexity explanation.

Drill 1: Valid Parentheses

Given a string containing bracket characters, return whether every opener is closed by the same bracket type in the correct order.

Hidden answer: invariant, tests, and Python solution

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

Drill 2: Daily Temperatures

Given daily temperatures, return how many days each position must wait until a warmer temperature. Use zero if no warmer day exists.

Hidden answer: monotonic-stack solution

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

Drill 3: Largest Rectangle In Histogram

Given bar heights, return the area of the largest rectangle that can be formed by contiguous bars.

Hidden answer: invariant, mistakes, and Python solution

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

Drill 4: Longest Repeating Character Replacement

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.

Hidden answer: sliding-window invariant

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

Drill 5: Minimum Window Substring

Given strings s and t, return the minimum window in s containing all characters of t with multiplicity.

Hidden answer: counts, edge tests, and Python solution

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]

Drill 6: Decode String

Decode strings such as 3[a2[c]], where a number before brackets repeats the bracketed substring.

Hidden answer: nested-state stack

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

How These Patterns Reappear In Speech ML

Follow-Up 1: Detect Queue Saturation Windows

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.

Hidden answer: interview direction

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.

Follow-Up 2: Parse Streaming Tool Calls

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?

Hidden answer: stack-based reasoning

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

Short Answers With Hidden Rubrics

Prompt 1: Explain Stale Max Frequency

In Longest Repeating Character Replacement, why can the window keep a stale maximum frequency instead of recomputing it after shrinking?

Hidden answer

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.

Prompt 2: Choose A Stack Or Window

You need to diagnose partial transcript churn over a stream. When is it a stack problem, and when is it a sliding-window problem?

Hidden answer

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.