Your country

Tools that support it use your country for local currency, number formats, units and paper size. Your choice is saved only in this browser.

Type a name or a two-letter code. Use the up and down arrow keys to move through the countries, Enter to choose one and Escape to close.

Coding Interview Patterns  Module 8 – Stacks and monotonic stacks

Greedy choices with a monotonic stack

Delete k digits to leave the smallest number, and build the smallest code with each letter once, by taking back earlier choices with a monotonic stack.

  • Intermediate
  • 20 minutes
  • Examples run with Python 3.14.8 and Pyodide 314.0.7
  • By MySmartCoPilot
Free intro, Pro: 4 of 20 minutes free
Free: The problem: the smallest reference number. With a pass: the other 9 sections and 3 interview answers, about 16 minutes.

What you will learn

  • Delete k digits of a number to leave the smallest possible number in O(n)
  • Prove that taking back a larger digit before a smaller one is safe
  • Handle leftover deletions, leading zeros and a result with no digits left
  • Build the smallest subsequence that uses each distinct letter exactly once
  • Test a greedy stack against an exhaustive search on short random inputs

Before you start

What you’ll learn · Before you start

What you will learn

  • Delete k digits of a number to leave the smallest possible number in O(n)
  • Prove that taking back a larger digit before a smaller one is safe
  • Handle leftover deletions, leading zeros and a result with no digits left
  • Build the smallest subsequence that uses each distinct letter exactly once
  • Test a greedy stack against an exhaustive search on short random inputs

Before you start

On this page

Some greedy problems build an answer from left to right and take back earlier choices: to delete two digits of 4253 and leave the smallest number, the 2 takes back the 4 and the 3 takes back the 5, which leaves 23. When a better item arrives, they undo the items before it that it beats, as far as a budget allows. A monotonic stack: A stack whose values stay in order from bottom to top, because each new item first pops every item it settles. It finds the nearest earlier or later larger, or smaller, item for every item in one pass. More in “Stack patterns: nesting and undo” holds the choices made so far, and its pops are the take-backs. The best-known case is deleting k digits from a number to leave it as small as possible: a digit followed by a smaller one should usually go, because the first place where two candidates differ decides which is smaller. One pass gives the answer in O(n), where trying every set of deletions grows exponentially. The same idea builds the smallest subsequence: Items taken from a sequence in their original order but not necessarily side by side: 1, 3, 4 is a subsequence of 1, 2, 3, 4. A substring or a subarray must be contiguous. More in “Which pattern? A decision guide” that uses each distinct letter exactly once. That makes a short code for a name: each letter once, in the name’s order, first in alphabetical order.

The problem: the smallest reference number

The problem

A receipt printer prints a code of n digits. To make a short reference number, exactly k of its digits are deleted and the others are kept in their order. Return the smallest number that can be left, written without leading zeros, or "0" when no digit, or only zeros, are left. Limits: 1 ≤ k ≤ n ≤ 100,000; the code is a string of n digits, and it may contain zeros anywhere.

Four examples, which every program in this lesson runs:

  1. "4253" with k = 2 gives "23".
  2. "30420" with k = 1 gives "420": deleting the 3 leaves 0420.
  3. "12345" with k = 2 gives "123".
  4. "9" with k = 1 gives "0": nothing is left.
Two codes with the deleted digits dashed: 4253 with k = 2 leaves 23, and 30420 with k = 1 leaves 0420, which reads as 420.k = 2: delete the 4 and the 54deleted2kept5deleted3kept23k = 1: delete the 3, then drop the leading 03deleted0kept4kept2kept0kept420

The digits kept stay in order; leading zeros do not count

Text description of the diagram

The figure shows the first two examples of the problem as rows of digit boxes. Deleted digits have dashed borders and kept digits solid ones.

  1. The code 4253 with k = 2: the 4 and the 5 are deleted, and the 2 and the 3 are kept, in their order, which leaves 23. No other choice of two digits leaves a smaller number: 25, 43, 45, 53 and 42 are all larger.
  2. The code 30420 with k = 1: the 3 is deleted and 0420 is kept. Read as a number it is 420, because the leading 0 is dropped.

In both rows, each deleted digit is larger than the digit right after it.

The brute force: try every set of deletions

Every candidate keeps n − k digits, so candidates of the same code can be compared as text, character by character: their lexicographic order: The order a dictionary uses: two sequences are compared at the first place where they differ, and the one with the smaller item there comes first. Numbers with the same number of digits compare this way as text. is their order as numbers. The brute force tries every set of positions to keep and returns the smallest:

Every way to keep n − k digits, and how many ways there are Python · brute/removals_brute.py
"""The smallest number left after deleting k digits, the brute force: try every set of k digits to delete."""

from itertools import combinations
from math import comb, lgamma, log


def smallest_after_removals_brute(code, k):
    """The smallest number (as text, without leading zeros) left after deleting exactly k digits of code."""
    best = None
    tried = 0
    for kept in combinations(range(len(code)), len(code) - k):  # the positions that stay, in order
        candidate = "".join(code[i] for i in kept)  # every candidate has n - k digits, so text order is number order
        tried += 1
        if best is None or candidate < best:
            best = candidate
    return best.lstrip("0") or "0", tried


print(smallest_after_removals_brute("4253", 2)[0])
print(smallest_after_removals_brute("30420", 1)[0])
print(smallest_after_removals_brute("12345", 2)[0])
print(smallest_after_removals_brute("9", 1)[0])

answer, tried = smallest_after_removals_brute("7051938204618372", 8)
print(f"16 digits, delete 8: {answer} after {tried:,} candidates")
for n in (20, 30, 40):
    print(f"{n} digits, delete {n // 2}: {comb(n, n // 2):,} candidates")
digits = int((lgamma(100_001) - 2 * lgamma(50_001)) / log(10)) + 1
print(f"100,000 digits, delete 50,000: a count of candidates with {digits:,} digits")

Output

23
420
123
0
16 digits, delete 8: 418372 after 12,870 candidates
20 digits, delete 10: 184,756 candidates
30 digits, delete 15: 155,117,520 candidates
40 digits, delete 20: 137,846,528,820 candidates
100,000 digits, delete 50,000: a count of candidates with 30,101 digits

Recorded with Python 3.14.8 on macOS 26 arm64. To run it yourself: mise exec python@3.14.8 -- python3 removals_brute.py

It is correct and quick for 16 digits, 12,870 candidates. The count is the binomial coefficient C(n, k), which is largest when k is half of n: 155 million candidates for 30 digits, 138 billion for 40, and for the limit of 100,000 digits a number that itself has 30,101 digits. No time limit allows that.

Checkpoint

Check: What trying every set of deletions costs

  1. One question How many candidates does the brute force compare for a code of 18 digits with k = 9?

    Type a number.

    A hint

    The count is the binomial coefficient C(n, k).

    Show the answer: One question

    Answer: 48620 candidates

    C(18, 9) = 48,620. The recorded run shows 184,756 for 20 digits with k = 10: two more digits multiply the count by nearly four.

What to notice

All candidates have the same length, so the leftmost digit where two of them differ decides which is smaller, and the leftmost digits matter most. A digit followed by a smaller one is a poor choice for its place: deleting it moves the smaller digit one place to the left, and nothing later makes up for a larger digit further left. So keep digits from left to right, and take back an earlier digit when a smaller one arrives, while deletions remain. Taking back the most recent choice first is the stack cue of this module.

Checkpoint

Check: Why the leftmost digits matter most

  1. One question The candidates 253 and 243 have the same length. Which is smaller, and what decides it?

    Choose one answer.

    Show the answer: One question

    Answer: 243, because at the first place where they differ, the second digit, 4 is below 5

    Two candidates of the same length compare like text: the first place where they differ decides. 243 and 253 differ first at the second digit, so 243 is smaller.

The questions below practise both ideas.

Practice that adapts to you

Practise this lesson

6 questions about 7 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.

  1. Question 1 of 6 How many candidates does the brute force compare for a code of n digits with k deletions?

    Choose one answer.

    Show the answer: Question 1 of 6

    Answer: C(n, k), the number of ways to choose the k digits to delete

    Every candidate keeps n − k digits in their order, so there is one for each way to choose the k digits to delete: C(n, k), 12,870 for 16 digits with k = 8.

  2. Question 2 of 6 For a code of 40 digits, which number of deletions makes the brute force compare the most candidates?

    Choose one answer.

    Show the answer: Question 2 of 6

    Answer: k = 20, half of the digits

    The binomial coefficient C(n, k) is largest when k is half of n: for 40 digits that is k = 20 and about 138 billion candidates.

  3. Question 3 of 6 The brute force is hopeless at 100,000 digits. What is it still good for?

    Choose one answer.

    Show the answer: Question 3 of 6

    Answer: As the judge of a faster method on short random codes, where it is quick and plainly right

    The brute force tries every candidate, so on short codes it is the judge: a faster method that disagrees with it on some input is wrong there.

  4. Question 4 of 6 Why is a digit followed by a smaller digit a poor choice for its place?

    Choose one answer.

    Show the answer: Question 4 of 6

    Answer: Deleting it moves the smaller digit one place to the left, and the leftmost digits matter most

    Compare the two candidates at the digit's place: with the digit deleted, the smaller digit sits there instead, and the first difference decides, so the number gets smaller.

  5. Question 5 of 6 For the code 1432 with k = 1, which digit should be deleted?

    Choose one answer.

    Show the answer: Question 5 of 6

    Answer: The 4, the first digit followed by a smaller one, which leaves 132

    The four candidates are 432, 132, 142 and 143. Deleting the 4, which is followed by the smaller 3, moves the 3 into the second place and gives the smallest, 132.

  6. Question 6 of 6 When a smaller digit arrives, why is the most recent kept digit the first one to take back?

    Choose one answer.

    Show the answer: Question 6 of 6

    Answer: The new digit sits right after it, so it is the one the new digit would move in front of

    The new digit follows the newest kept digit directly, so that is the comparison that matters first; only once it is taken back does the digit before it come next. Newest first is the stack's order.

A stack takes back larger digits while deletions last Pro: opens with a Pro, Premium or Ultimate pass

In this part:

  • A dry run on 4253 with k = 2
  • Why taking back a larger digit is safe: the proof
  • The code in 14 languages, each with its own stack of characters
  • Slips, edge cases and a stress test
  • The smallest code that uses each letter once
  • Practise: the smallest department code
    • Build the smallest code that uses each letter once
  • Variants and follow-up questions, answered
  • Interview questions
    • Explain why deleting a digit that is larger than the digit after it never makes the result worse, and turn that into a proof that the stack method returns the smallest number.
    • Your remove-k-digits solution passes the examples but fails hidden tests. List the edge cases you would test, the bug each one catches, and the smallest input for each.
    • A code of 10⁹ digits arrives as a stream, and exactly 10⁶ of its digits must be deleted to leave the smallest number, which you write out as you go. How much memory do you need, and when can a digit be written?
  • Key takeaways

2 diagrams · 3 runnable examples (Python) · 1 exercise with sample tests · 3 interview answers · about 35 minutes

Opening this part with your pass…

This part opens with any MySmartCoPilot pass.

  • Pro pass · 1 week₹79$2.99
  • 30 days₹179$5.99
  • 1 year₹1,699$45

One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.

It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).

Terms in this lesson

lexicographic order
The order a dictionary uses: two sequences are compared at the first place where they differ, and the one with the smaller item there comes first. Numbers with the same number of digits compare this way as text.

References

Related tools

Report a problem with this lesson

Quick answers and tool search

Type to search tools or to get a quick answer, for example 18% of 2500. Use the up and down arrow keys to move through the results, Enter to choose, and Escape to close.