给你一个以字符串表示的非负整数 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 不含任何前导零
思路:
本题采用贪心 + 单调栈方法
「删除一个数字」的贪心策略:
给定一个长度为 n 的数字序列 [D0 D1 D2 D3 … Dn−1],从左往右找到第一个位置 i( 0<i<n)使得 Di<Di−1, 此时删除 Di-1 可以使得删除后的数字序列最小 ;如果不存在,说明整个数字序列单调不降,那么删去最后一个数字即可。
题目中要求的是删除 k 个数字,思路是这样的
1. 定义一个 nums_stack 单调栈
2. 从 数字序列的左边开始,将数字一次压入栈中,压入前,判断当期数字num[i] 是否比栈顶元素小,若是小的话,便将栈顶数字弹出,循环遍历直到栈顶元素不再大于 num[i] 为止。此时可以将当前元素压入 num_stack (考虑一下 0 的情况,若是num[i] == '0' ,则不压入栈中,避免出现 00123 的情况), 栈中每弹出一个数字视作删除了一个数字,记录下栈弹出的次数
3. 2步骤 中 若是删除的数字个数小于 k ,那么退出 2 中的循环后,继续弹出栈数字,直到满足删除的数字个数为k为止
c++
class Solution {
public:
string removeKdigits(string num, int k) {
if(num.empty()) {
return num;
}
stack<char> nums_stack;
int i = 0, count = 0;
// 单调栈框架
while(i < num.size()) {
// 删除大于 num[i] 且在 num[i] 左边的数字
while(count < k && !nums_stack.empty() && nums_stack.top() > num[i]) {
nums_stack.pop();
count += 1;
}
// 避免出现 000123 的情况, 所以最高位不能是 0
if(nums_stack.empty() && num[i] != '0' || !nums_stack.empty()) {
nums_stack.push(num[i]);
}
i++;
}
// 经过前面的循环,nums_stack 中的数字变成了单调非递减,此时若是还有数字可以删除,那么久直接从最右边(即nums_stack 的栈顶)开始删除即可
while(!nums_stack.empty() && count < k) {
nums_stack.pop();
count++;
}
string result;
while(!nums_stack.empty()){
result += nums_stack.top();
nums_stack.pop();
}
// 翻转字符串
int L = 0, R = result.size()-1;
while(L < R) {
char tmp = result[L];
result[L] = result[R];
result[R] = tmp;
L++;
R--;
}
// 说明删除后k个数字后,剩下的数字都是 0
if(result.empty()) {
return "0";
}
return result;
}
};