难度中等
给你一个以字符串表示的非负整数 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
不含任何前导零
# 贪心 + 单调栈
若只删除 次。
例如: ,那么肯定是删除
若删除 后面的元素 其中一个,那么最后肯定是前缀为 的序列
若删除 ,那么前缀变成 的序列
二者的长度必然相等,因为都是删除 次
贪心地选择删除
例如:,那么肯定删除
对于 后面的元素与上述同理
若删除 前面的元素 其中一个,那么最后 或者 的序列必然大于 的序列
贪心地选择删除
删除 后的序列 ,若还可以删除继续删除,那么必然删除 ,分析同上。
若是连续递增的序列,那么不必删除
若当前元素 小于结尾的元素,那么不断地删除结尾的元素直至 大于结尾元素,最后将 加入结果集的尾部
- 例如:,,那么需要不断地删除结尾元素 至 至
最多只能删除 次,若 那么可以直接退出循环,将 中剩余地元素放入结果集
对于 ,
根据上述步骤删除了 此时结果集 ,最后将 加入结果集。
为什么不删除 其中一个?
上文已经分析了,对于 :
若删除 以后的元素 其中一个,那么最后肯定是 的序列
若删除 ,那么最后是 的序列
此时后者必然小于前者,贪心的选择后者,即:删除
也同理
若 最后还有剩余,那么可以直接删除尾部的 个元素,若结果集不足 个,那么直接返回
我们可以用栈来表示结果集,可以发现这是个单调递增栈。
- 即:栈顶元素 大于 ,那么不断地删除栈顶元素并更新 直至栈顶元素 。将 压入栈顶。
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); | |
} | |
} |