移掉K位数字

题目来自acwing

题目(点击跳转)

给定一个以字符串表示的非负整数 num,移除这个数中的 k 位数字,使得剩下的数字最小。

注意:

  • 空字符串被视为0。
  • 如果结果中包含前导零,则需要将前导零删除,最后删除的前导零不用包含在移除的 k 个数字中。

输入格式

第一行输入一个字符串,用来表示非负整数 num。

第二行输入一个整数,表示 k。

输出格式

输出一个字符串,表示移除 k 位数字后所能得到的最小数字。

数据范围

0≤k≤ 字符串长度 ≤100000,
num 中不包含任何前导 0。

输入样例1:

1
2
1432219
3

输出样例1:

1
1219

样例1解释

移除掉三个数字 4,3,2 可形成一个新的最小的数字 1219。

输入样例2:

1
2
10200
1

输出样例2:

1
200

样例2解释:

移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零

输入样例3:

1
2
10
2

输出样例3:

1
0

样例3解释

从原数字移除所有的数字,剩余为空就是 0。

思路:

这道题的思路是贪心,只需要删除数字的时候使得删除时候的数字小于删除其他数字的结果即可。数字字符串分下面两种情况:

  • 从前往后是递增的
    • 那么这种情况下从后面删除数字肯定是最优解,即从后往前删除k位即可。
      • 如果从前面删除,那后面的数字会往前移动,由于是递增的序列,往前移动值肯定会变大。
  • 从前往后是递增的,遇到了逆序,也就是1232,这时32即为逆序。
    • 当遇到逆序时只需要把逆序的数字删掉即可
      • 也就是1232删除一位,那肯定是删除3得到的结果最小
      • 1453,如果删除两位的话,即删除5和4,当遍历到3时遇到了逆序,看前一位大于3,则删除5,再看前一位大于3则删除4,再看前一位1小于3,将3存下来,所以遇到逆序时如果可以则删除到无逆序为止,最后如果k还大于0就从后面删除。
  • 最后将前导0删除即可
    • 从0开始遍历,然后如果为0则向后移动,最后输出的时候将0后面输出即可。

代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

int main() {
string str;
int k;
string res = "0";
cin >> str >> k;
for(int i = 0; i < str.size(); i ++ ) {
while(k && str[i] < res.back()) {
res.pop_back();
k --;
}
res.push_back(str[i]);
}
// cout << res << ' ' << k << endl;
while(k -- ) res.pop_back();
int i = 0;
while(i < res.size() && res[i] == '0') i ++;
// cout << i << endl;
if(i == res.size()) puts("0");
else cout << res.substr(i) << endl;

return 0;
}