题目
分析
这道题目的有趣之处在于:题意很容易理解,一般也能想到“贪心”,但是怎么贪心呢?
我们先看几道例题,我们先看一个5位数,拿掉一个数字:
12345
=> 1234
。观察到:所有数字顺序排列,那么最后一个数字(也就是最大的数字)可以被删除。
12354
=> 1234
。观察到:数字5
出现在4
之前(逆序对),那么就要删除5
而不是4
。
越大的数字位数越高(这里是越早出现),构成了逆序对,这样的数字应该尽早拿掉。换句话说,我们要构造一个高位(早出现)尽可能小、低位(晚出现)尽可能大的数字。
核心做法:
- 从左到右扫描
- 如果
i
位和i+1
位的数字构成逆序对:去掉i
,保留i+1
。否则保留i
和i+1
; - 如此操作后如果还需要拿走数字,那么从最后开始去掉。
这是因为,当我们扫描完所有数字并消除了所有逆序对后,如果还有剩余的删除次数,那么剩下的数字呈现非递减(不会递减)的特性。这意味着从左到右,数字要么增加,要么保持不变。
我们保留的数字中,一定是在最高位保留了较小的数字——符合题目的要求。
用栈来实现
这种比较、踢出、保留的描述和栈是一致的。所以,我们应该用栈来实现。
// 从左到右扫描数字
for (char digit : n)
{
// 如果当前数字比栈顶元素小,且还有删除次数,则删除栈顶元素
while (!stack.empty() && stack.back() > digit && k > 0)
{
stack.pop_back();
k--;
}
stack.push_back(digit);
}
// 如果还有删除次数,从栈的末尾删除
while (k > 0 && !stack.empty())
{
stack.pop_back();
k--;
}
答案
思考
请认真阅读代码(22-31行),这是本题解法的关键。