1、问题描述
给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。
注意:
- num 的长度小于 10002 且 ≥ k。
- num 不会包含任何前导零。
示例 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。
2、思考与分析
3、贪心规律
4、使用栈来优化求解
5、需要注意的问题
- 当所有数字都扫描完成后,k仍然>0,应该做怎样的处理? 例如: num = 12345, k = 3 时。
- 当数字中有0出现时,应该有怎样的特殊处理? 例如: num = 100200, k = 1 时。
- 如何将最后结果存储为字符串并返回?
6、代码实现
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Solution
{
public:
string removeKdigits(string num, int k)
{
vector<int> S; // 使用vector当作栈,方便遍历操作
string result = ""; // 存储最终结果
for (int i = 0; i < num.length(); i++) // num.size() is ok!
{
int number = num[i] - '0'; // 数字字符转整型数字 '3' -> 3
// 这个循环的作用是:在k还有机会的情况下(k > 0),
// 要将S中,从后到前,比number大的全部删除掉
while (S.size() != 0 && S[S.size() - 1] > number && k > 0)
{
// S.size() != 0 说明还没有将num的字符压入到S中,或者被弹空了
// S[S.size() - 1] > num 说明新来的数字小,比当前S尾部的元素值更有利于使整体变小
// k > 0 说明还有机会删除数字
S.pop_back(); // 当上述三个条件都满足时,弹出S尾部元素
k --; // 用掉一次弹出数字的机会
}
// 如果上述循环不满足条件
// 如果number != 0 ,那就可以将其压入
// 如果number == 0,但只要S.size() != 0就可以将其压入
// 这两点保证是因为,数字不能以一个0或者一串连续的0开头
// if (number != 0 || S.size() != 0)
if (number != 0)
{
// 只所以能到这一步,是因为比该number大的,在k > 0范围内,全部被干掉了,
// 当前S.size()会终止一下
// 不过当前number是0的话,虽然全部把前面的干掉了,但是0不能当作首元素
S.push_back(number);
}
}
// 这种情况是,k的次数没有用完,就像12345678这种一样
// 注意:这里的S.size() != 0 是有必要的,因为最后可能只剩了尾部的0
// 而这些0是没有压入的S中,所以这种情况就是,S为空,但k > 0还满足,num也遍历完了
while (S.size() != 0 && k > 0)
{
// 只能从尾部用剩下的机会删除
S.pop_back();
k --;
}
// 到目前位置,S中剩下的元素,就是经过元素删除后满足条件的
for (int i = 0; i < S.size(); i ++)
{
result.append(1, '0' + S[i]);
}
if (result == "")
{
result = "0";
}
return result;
}
};
int main()
{
string num = "10200";
int k = 1;
Solution S;
string res = S.removeKdigits(num, k);
cout << res << endl;
return 0;
}