402. 移掉 K 位数字

难度中等

给你一个以字符串表示的非负整数 num 和一个整数 k ,移除这个数中的 k 位数字,使得剩下的数字最小。请你以字符串形式返回这个最小的数字。

示例 1 :

输入:num = "1432219", k = 3

输出:"1219"

解释:移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219 。

示例 2 :

输入:num = "10200", k = 1

输出:"200"

解释:移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

示例 3 :

输入:num = "10", k = 2

输出:"0"

解释:从原数字移除所有的数字,剩余为空就是 0 。

提示:

  • 1 <= k <= num.length <= 105
  • num 仅由若干位数字(0 - 9)组成
  • 除了 0 本身之外, num 不含任何前导零

# 贪心 + 单调栈

若只删除 11 次。

例如: 14322191432219,那么肯定是删除 44

  • 若删除 44 后面的元素 3221932219 其中一个,那么最后肯定是前缀为 1414\cdots 的序列

    若删除 44,那么前缀变成 1313\cdots 的序列

    二者的长度必然相等,因为都是删除 11

    贪心地选择删除 44

例如:14522191452219,那么肯定删除 55

  • 对于 55 后面的元素与上述同理

  • 若删除 55 前面的元素 1414 其中一个,那么最后 1515\cdots 或者 4545\cdots 的序列必然大于 1414\cdots 的序列

    贪心地选择删除 55

  • 删除 55 后的序列 142219142219,若还可以删除继续删除,那么必然删除 44,分析同上。

若是连续递增的序列,那么不必删除

若当前元素 s[i]s[i] 小于结尾的元素,那么不断地删除结尾的元素直至 s[i]s[i] 大于结尾元素,最后将 s[i]s[i] 加入结果集的尾部

  • 例如:145145s[i]=2s[i] =2,那么需要不断地删除结尾元素 145145\cdots142142\cdots1212\cdots

最多只能删除 kk 次,若 k==0k == 0 那么可以直接退出循环,将 ss 中剩余地元素放入结果集

对于 14322191432219k=2k = 2

根据上述步骤删除了 4,34,3 此时结果集 1212 ,最后将 219219 加入结果集。

为什么不删除 219219 其中一个?

上文已经分析了,对于 33

  • 若删除 33 以后的元素 22192219 其中一个,那么最后肯定是 143143\cdots 的序列

  • 若删除 33,那么最后是 142142\cdots 的序列

    此时后者必然小于前者,贪心的选择后者,即:删除 33

44 也同理

kk 最后还有剩余,那么可以直接删除尾部的 kk 个元素,若结果集不足 kk 个,那么直接返回 00

我们可以用栈来表示结果集,可以发现这是个单调递增栈。

  • 即:栈顶元素 peekpeek 大于 s[i]s[i],那么不断地删除栈顶元素并更新 k=k1k=k-1 直至栈顶元素 peek<=s[i]peek <= s[i]。将 s[i]s[i] 压入栈顶。
class Solution {
    public String removeKdigits(String num, int k) {
        int n = num.length();
        StringBuilder st = new StringBuilder();
        for (int i = 0; i < n; i++) {
            char ch = num.charAt(i);
            while (k > 0 && st.length() > 0 && st.charAt(st.length() - 1) >= ch) {
                st.deleteCharAt(st.length() - 1);
                k--;
            }
            // 除去前导零
            if (st.length() > 0 || ch != '0') {
                st.append(ch);
            }
        }
        int len = st.length();
        if (len <= k) {
            return "0";
        }
        return st.substring(0, len - k);
    }
}