题目描述:
Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible.
Note:
- The length of num is less than 10002 and will be ≥ k.
- The given num does not contain any leading zero.
Example 1:
Input: num = "1432219", k = 3 Output: "1219" Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest.
Example 2:
Input: num = "10200", k = 1 Output: "200" Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes.
Example 3:
Input: num = "10", k = 2 Output: "0" Explanation: Remove all the digits from the number and it is left with nothing which is 0.
程序代码:
class Solution {
public:
string removeKdigits(string num, int k) {
int len = num.size();
// 用一个双向队列帮助完成算法
deque<int> q;
// total为去掉k个字符后剩余的字符总数(k表示需要去掉的字符数)
int total = len - k;
// 若串长度<=k, 直接输出0
if (len <= k) {
string res = "0";
return res;
}
// 遍历
for (int i = 0; i < len; i++) {
// 若双向队列队尾的元素比当前访问的字符更大,则将其去掉,并将需要去掉的字符数量-1(即k-1)
// 循环,直到队列为空,或队尾元素不会比当前访问元素更大,或是k == 0(即不需要再去掉字符了)
while (k != 0 && !q.empty() && q.back() > num[i]) {
q.pop_back();
k--;
}
// 将当前访问元素放入队尾(有可能会多出来,之后再处理)
q.push_back(num[i]);
}
// 将队列中元素放入一个字符串
// 此外,只需要放入队列中前total个元素即可,多余的后面部分舍弃掉即可
string res = "";
for (int i = 0; i < total; i++) {
res += q.front();
q.pop_front();
}
// 去除结果开头的0
int j = 0;
while (res[j] == '0') {
j++;
}
res = res.substr(j);
// 至少要输出一个“0”而不是空串
if (res == "") {
res = "0";
}
return res;
}
};
本题依旧用到了这两周所学的贪心算法。
先理解题意。根据题目描述及所给例子可以看出,本题是要我们去掉一个字符串中的k个字符,使剩下的字符为可能的最小值。需要注意两点,一是最后结果前的0不需要输出,二是如果k与字符串总长度相等,则要输出0.
考虑如何解这个题目。按常规的方法来想,可以通过遍历整个数组,每次如果发现某个字符比它的前一个字符更大,则将这个字符删去,然后重头开始循环;若没有符合条件的字符,则在遍历到最后时将末尾字符删去,并重头开始循环。这整个操作重复k次。这样就可以得到题目要求的答案。不过这种算法每删除一个字符就要再重头遍历一次,显然时间复杂度会比较大。
考虑进一步优化贪心算法。可以使用一个双向队列来辅助完成。遍历整个字符串,将每个字符都放入双向队列尾部,不过在放入之前,先要判断(若双向队列不为空的话)双向队列尾部的字符是否比遍历访问到的这个字符大,若是,则说明队列尾部这个字符删去的话会让结果变小,故将其删去,并将k-1(表示需要删除的字符数量少了一个)重复这个操作直到队列为空、或是队尾字符不再比访问到的字符更大、或是k次删除次数全部用完(k==0)。当全部的字符都放入双向队列后遍历结束。此时队列中的元素数量有可能比题目要求的结果更多,接着只要将这个双向队列的前len - k 个元素放入一个空字符串即可(len为原字符串长度,len - k 表示删去k个字符后剩余的长度),队列中后面部分的元素舍弃即可(相当于上面常规解法中的遍历时删除末尾字符)。最后,为了满足输出要求,需要将字符串前面的0前缀去掉,并输出最终结果。
本题是一道结合了字符串的贪心算法问题,算法中每次只考虑局部的一个字符与前面字符的关系,但可以最终得出正确的结果。贪心算法确实非常有用,不过有时也不容易想到如何使用,需要多加巩固。