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'.
- 1 <= k <= num.length <= 10⁵
- num consists of only digits
- num does not have any leading zeros except for '0' itself
num = "1432219", k = 3"1219"num = "10200", k = 1"200"num = "10", k = 2"0"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".
# 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.
Monotonic Stack Reduction Strategy
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.
Edge Cases
1. Remove trailing digits if kInitial > 0. 2. Handle leading zeros. 3. Return "0" if empty.