主要思路;由最高位开始,比较低一位数字,如高位大,移除,若高位小,则向右移一位继续比较两个数字,直到高位大于低位则移除,循环k次。
多次遍历
但是这题的特殊情况比较难处理,可以暴力采取每次都从头遍历,但是复杂度会增加:
class Solution {
public:
string removeKdigits(string num, int k) {
if(num.size() == k) return "0";
for(int i = 0; i < k; ++i){
int j = 0;
while(j < num.size()-1 && num[j] <= num[j+1]) ++j;
if(j >= num.size()) num.erase(num.size()-1, 1);
else num.erase(j, 1);
}
int beg = 0;
while(num[beg] == '0') ++beg;//取出前导0
string res = num.substr(beg);
return res == "" ? "0" : res;
}
};
这样的效率一般,因为每次都从头遍历。
一次遍历
优化如下:
class Solution {
public:
string removeKdigits(string num, int k) {
if(num.size() == k) return "0";
int j = 1, flag = 1;
for(int i = 0; i < k; ++i){
if(flag == 1){
while(j < num.size() && num[j-1] <= num[j]) ++j;
num.erase(j-1, 1);
if(j > 1) --j;//因为删除了一个嘛,退回去让j指向之前指向的元素
if(j == num.size()) flag = -1;//走到了最后,那么后续就只能从末尾依次删除了
}else num.erase(num.size()-1, 1);
}
int beg = 0;
while(num[beg] == '0') ++beg;//取出前导0
string res = num.substr(beg);
return res == "" ? "0" : res;
}
};
单调栈
不过上述代码还是不够快,因为删除操作还是比较耗时的。还可以借助栈操作,参考
移掉K位数字 - 移掉K位数字 - 力扣(LeetCode)
class Solution {
public:
string removeKdigits(string num, int k) {
stack<char> s;
for(int i = 0; i < num.size(); ++i){
while(!s.empty() && num[i] < s.top() && k != 0){//注意这儿是while循环
s.pop();
--k;
}
if(s.empty() && num[i] == '0') continue;//跳过前导0
s.push(num[i]);
}
string res;
while(!s.empty()){
if(k > 0) --k;//移除未结束,从栈顶依次移除
else if(k == 0) res += s.top();//移除结束,生成结果串
s.pop();
}
reverse(res.begin(), res.end());
return res == "" ? "0" : res;
}
};
不用栈,直接用字符串操作,一样的思路:
class Solution {
public:
string removeKdigits(string num, int k) {
string res;
for(int i = 0; i < num.size(); ++i){
while(!res.empty() && num[i] < res.back() && k != 0){//注意这儿是while循环
res.pop_back();
--k;
}
if(res.empty() && num[i] == '0') continue;//跳过前导0
res += num[i];
}
while(k > 0 && !res.empty()){
res.pop_back();
--k;
}
return res == "" ? "0" : res;
}
};
其实这个思路和一次遍历的那个,本质上差不多,不过那个删除操作比较耗时。