402. 移掉k位数字

链接

https://leetcode.cn/problems/remove-k-digits/description/

思路

这个题目要求移除k位后,剩下的数字最小。既然剩下的数字最小,那就牵扯到了类似于“字典序”这样一个概念。这样的题目是适合用单调栈来进行解决的。

依然是单调栈的三板斧:

1. 什么情况下我们弹栈。

2. 栈内元素是(部分)升序还是(部分)降序

3. 我们应该从前往后遍历,还是从后往前遍历

首先回答第二个问题,栈内元素应当是升序的,因为我们要求移除之后剩下的数字最小。

然后回答第三个问题,我们应该从前往后遍历。因为在一串数字中,左侧的比较大的数字对最后结果的影响最大,所以应该先处理左边。

最后回答第一个问题,我们应该遇到栈顶元素大于待入栈元素时弹栈,以保证单调栈中的元素是升序的。但这里有个k的限制,所以,当我们的k用完之前,我们可以弹栈,用完之后就不可以弹栈了。同时,如果栈顶元素是0时,我们不可以弹栈。因为0是前导0,我们在生成单调栈的过程中不需要处理。

在得到稳定的栈之后,我们还要判断k有没有用完,分别进行处理。

所以我们可以得到代码一。

但代码一并不够优雅。

第一点,我们既然要去掉k个字符,那我们就必然最多剩余remains = len(s)-k个字符。(这个是带着前导0的),这样我们就不需要在最后还去判断k的状态了。

第二点,我们在while循环中不用对0进行处理,为什么呢,因为栈顶元素大于待入栈元素,隐含了栈顶元素如果为0时不可能命中这个条件。

根据这两点可以得出代码二。

代码一

class Solution:
    def removeKdigits(self, num: str, k: int) -> str:
        single_stack = []
        for i in num:
            # k有额度时才能弹栈
            # 注意,这里用的是隐含ord比较,不需要转换成int,即可大大减少耗时
            while k > 0 and single_stack and single_stack[-1] > i:
                # val不是0的话, 就对k进行处理
                val = single_stack[-1]
                if val != '0':
                    k -= 1
                    single_stack.pop()
            single_stack.append(i)
        res = ''.join(single_stack).lstrip('0')
        # k超限了, 证明需要返回0
        if k >= len(res):
            return '0'
        # k没超限制, 对res进行截尾
        elif k > 0:
            return res[:-k]
        return res

 

代码二

class Solution(object):
    def removeKdigits(self, num, k):
        stack = []
        remain = len(num) - k
        for digit in num:
            while k and stack and stack[-1] > digit:
                stack.pop()
                k -= 1
            stack.append(digit)
        return ''.join(stack[:remain]).lstrip('0') or '0'

 

posted @ 2023-10-08 11:24  BJFU-VTH  阅读(9)  评论(0编辑  收藏  举报