LeetCode:402 移掉k位数字 贪心 / 单调栈

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

注意:
num 的长度小于 10002 且 ≥ k。
num 不会包含任何前导零。

示例 1 :
输入: num = “1432219”, k = 3
输出: “1219”
解释: 移除掉三个数字 4, 3, 和 2 形成一个新的最小的数字 1219。

示例 2 :
输入: num = “10200”, k = 1
输出: “200”
解释: 移掉首位的 1 剩下的数字为 200. 注意输出不能有任何前导零。

示例 3 :
输入: num = “10”, k = 2
输出: “0”
解释: 从原数字移除所有的数字,剩余为空就是0。

来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/remove-k-digits
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

这题一看可以bfs,因为每次删除一个字符,即每层的走法是固定的,那么走到最后,一共有 Ckn 种解法,找最小即可,但是这么做显然超时

然后换思路:因为贪心具有无后效性,那么每次bfs,不走所有的路,而是走结果最小的路即可,即每次枚举位置去掉一个字符,然后选择最小的数字,作为下一次的起点

这样做的仍然会超时,因为涉及字符的比较,复杂度为O(knn),其中k是k次删除,n是每次删除枚举位置,另一个n是比较字符时候

其实正确的思路是:我们想尽量删除高位的大字符,那么我们从最高位开始向右遍历,找到第一个使得 num[i]>num[i+1] 的位置 i,删除 位置 i ,这个操作重复k次

证明:
43125 第一个递减发生在下标0,删除0之后的结果是 3125

扫描二维码关注公众号,回复: 10088849 查看本文章

如果不删除0,那么得到结果:
4125,4325,4314,4312

可以看到如果不删除递减发生的下标那么无论怎么删除都不会得到最小值,因为高位永远有一个比较大的数字

特例:
如果是递增序列,比如 1234 那么只能删除最后一个下标,要单独处理

O(n) 优化:

  • 可以引入一个单调栈,栈顶保存每次遍历到的最高
  • 如果一个新元素要进栈,那么从栈顶开始,逐个删除比新元素大的栈元素,直到空,或者删除的次数用尽
  • 然后新元素进栈
  • 如果遍历完之后,删除次数还有剩余,删除栈顶元素,这对应着升序序列的情况,比如1234

单调栈证明

如果一个数字是 132,在三个元素中,如果中间元素比两边大,那么删除中间,得到的一定是最小的数字

这就是为什么可以使用单调栈,因为每次删除的都是三个元素中的中间位置,通过单调栈快速地找到了左右第一个小于它的值

证明:
a b c 组成数字 = a*100 + b*10 + c*1,且 b>a , b>c

  • 如果删除a,变成:b*10 * c*1
  • 如果删除b,变成:a*10 * c*1
  • 如果删除c,变成:a*10 * b*1

因为有 b>a 且 b>c ,那么最小的值肯定是 删除 b

代码

复杂度O(n) ,单调栈

class Solution {
public:
    string removeKdigits(string num, int k)
    {
        if(k==0) return num;
        if(k==num.length()) return "0";
        stack<char> s;
        for(int i=0; i<num.length(); i++)
        {
            while(!s.empty() && s.top()>num[i] && k>0) 
                s.pop(), k--;
            s.push(num[i]);
        }
        while(k--) s.pop();
        string ans = "";
        while(!s.empty()) ans+=s.top(), s.pop();
        while(ans.length()>1 && ans.back()=='0') ans.pop_back();
        reverse(ans.begin(), ans.end());
        return ans; 
    }
};

复杂度O(nk)

class Solution {
public:
    string no_zero(string s)
    {
        int i;
        for(i=0; i<s.length()-1; i++)
            if(s[i]!='0') break;
        return string(s.begin()+i, s.end());
    }
    string removeKdigits(string num, int k)
    {
        if(k==0) return num;
        if(k==num.length()) return "0";
        for(int t=0; t<k; t++)
        {
            int flag = 0;
            for(int i=0; i<num.length()-1; i++)
            {
                if(num[i]>num[i+1]) 
                {
                    num.erase(num.begin()+i);
                    flag = 1;
                    break;
                }
            }
            if(flag==0) num.pop_back();
        }
        return no_zero(num);
    }
};

超时代码

class Solution {
public:
    string no_zero(string s)
    {
        int i;
        for(i=0; i<s.length()-1; i++)
            if(s[i]!='0') break;
        return string(s.begin()+i, s.end());
    }
    string removeKdigits(string num, int k)
    {
        if(k==0) return num;
        if(k==num.length()) return "0";
        for(int t=0; t<k; t++)
        {
            string minNum = num;
            minNum.erase(minNum.begin());
            for(int i=1; i<num.length(); i++)
            {
                string ne = num;
                ne.erase(ne.begin()+i);
                if(minNum.compare(ne)>0) minNum=ne;
            }
            num = minNum;
        }
        return no_zero(num);
    }
};
发布了238 篇原创文章 · 获赞 7 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44176696/article/details/104918005