Algorithm

Remove K Digits

Monotonic Stack Pattern

Remove K Digits

Given string num representing a non-negative integer, and an integer k, return the smallest possible integer after removing k digits from num. Note: The result should not have leading zeros. If the result is an empty string, return '0'.

CONSTRAINTS
  • 1 <= k <= num.length <= 10⁵
  • num consists of only digits
  • num does not have any leading zeros except for '0' itself
EXAMPLE 1
Input: num = "1432219", k = 3
Output: "1219"
The digits 4, 3, and one 2 are removed as they are identified as local peaks relative to their successors.
EXAMPLE 2
Input: num = "10200", k = 1
Output: "200"
Removing the 1 creates the sequence '0200', which simplifies to '200' after stripping leading zeros.
EXAMPLE 3
Input: num = "10", k = 2
Output: "0"
All digits are removed, resulting in an empty state which defaults to '0'.
What if the string is already in increasing order?
If the string is like '12345', no peaks will be found. The while-loop at the end will simply chop off the last k digits, leaving the smallest possible prefix.

To make a number as small as possible, you want the smallest digits to be in the most "powerful" positions (the leftmost spots). A brute force approach would involve generating all possible combinations of removing k digits and comparing them, but this would lead to an exponential time complexity that is unusable for large numbers.

The core insight is Monotonic Greedy. We want to keep our digits in a non-decreasing order from left to right. If we ever see a digit that is smaller than the one before it, the previous digit is a "peak" that is making our number larger than it needs to be. For example, in "43XX", removing the 4 to get "3XXX" is always better than any other single removal, because it puts a smaller digit in a higher positional place.

The strategy works as follows:
- Scan and Compare: We use a stack to build our new number digit by digit. For each new digit, we check if it is smaller than the top of our stack.
- Pop the Peaks: If the new digit is smaller and we still have a removal budget (k > 0), we "pop" the larger digit from the stack. This greedily ensures the leftmost digits are as small as possible.
- Chop Leftovers: After scanning the whole string, if we still have budget left (e.g., the input was already sorted like "1234"), we simply remove digits from the end of the stack.
- Strip Zeros: Finally, we convert the stack back to a string and strip any leading zeros. If the result is empty, we return "0".

python
# Monotonic Greedy (O(N) Time, O(N) Space)
stack = []
for digit in num:
    # While current digit is smaller than the previous one
    while k > 0 and stack and stack[-1] > digit:
        stack.pop()
        k -= 1
    stack.append(digit)

# If k is still > 0, remove from the end
final_stack = stack[:-k] if k > 0 else stack

# Join and strip leading zeros
res = "".join(final_stack).lstrip('0')
return res if res else "0"

By using a stack, we only process each digit a few times, resulting in a perfect linear time complexity. The stack's LIFO property allows us to "travel back in time" to remove peaks as soon as a smaller challenger appears.

Worked Example:num = "14322", k = 2
0
1
1
4
Top
Push '1', push '4'. Stack = ['1', '4'].
0
1
1
3
Top
Read '3' < '4'. Pop '4' (k becomes 1). Push '3'. Stack = ['1', '3'].
0
1
1
2
Top
Read '2' < '3'. Pop '3' (k becomes 0). Push '2'. Stack = ['1', '2'].
0
1
1
2
2
2
Top
Read '2'. k is 0, so no more deletions. Push '2'. Stack = ['1', '2', '2']. Join stack to get final string: '122'.
Interactive Strategy Visualization
GREEDY MINIMIZATION ENGINE

Monotonic Stack Reduction Strategy

1
4
3
2
2
1
9
Small Number Builder
Removals
3

Mental Model

  • Greedy Choice: Smaller digits at the start (most significant positions) make the number smaller.
  • Descending Peak: If we find a smaller digit than the previous one, the previous one must be removed.
LOGICSTEP 1/12
Process '1432219' with k=3.
TIP

Edge Cases

1. Remove trailing digits if kInitial > 0. 2. Handle leading zeros. 3. Return "0" if empty.

O(2ᴺ) Try Every Removal
O(N) Monotonic Stack + Budget