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。

我首先看到这道题就觉得不对劲,删除字符使得剩余的字符构成的值最大,那怎么删除呢?删除的规律是什么?难道是通过DFS一次一次的尝试吗?好像不太可能!如果有字符串足够长那么组合的种类就非常大,最后肯定会tle!看看的我的解答过程!!!!!

还有一页....

菜逼就是这样不断的失败,不断的尝试,然后还是不断的失败!!!O(∩_∩)O哈哈~!那么第一版的思路如何呢?

class Solution {
    public String removeKdigits(String num, int k) {
          	int len = num.length();
    	if(len==k)return "0";
    	int count = 0;
    	char[] charArray = num.toCharArray();
        for(int i = 0,j=k;i<len;i++){
        	if(count==k)break;
        	if(charArray[i]<=charArray[j])continue;
        	else{
        		charArray[i]='f';
        		count++;
        	}
        	if(j<len-1)++j;
        }
        String s = new String(charArray);
        s = s.replaceAll("f", "");
        while(s.startsWith("0")&&s.length()!=1)
        	s=s.substring(1,s.length());
        return s;
    }
}

首先肯定拒绝DFS算法,那么就是找规律了,思路如下图

大概就像这个图一样,在1432219中我们要删除3个字符,那么一定会保留4个字符,删除肯定是先删除左边的因为左边的是高位.好像这样结果1219结果对了.但其实换一个数字例如1432319的结果是1319却是错误的.后面我也连续改了几版,但是总的思路都是错的.所以无论怎么改都是错的.

改了几版之后,只有重新寻找其他的解法.举个栗子我们要从长度为100的字符串里删除50个字符使得最后的结果值最小,那么我一次一次地删除,第一次删除1个使得结果最小,第二次再删除一个......,如此在删除50个字符后得到的结果会是最小的吗?答案是肯定的.这也是DP(动态规划)的思想.那么应该怎么删除呢?第一次改版后的错误版本如下:

class Solution {
    public String removeKdigits(String num, int k) {
	    int len = num.length();
    	if(k>=len)return "0";
    	int count = 0;
    	char indexChar;
    	while(count<k){
    		indexChar = num.charAt(0);
    		for(int i = 0;i < num.length();i++){
                if(num.charAt(i)>indexChar){
    				num = num.replaceFirst(num.charAt(i)+"", "");
    				break;
    			}
    		}
    		count++;
    	}
        while(num.startsWith("0")&&num.length()!=1)
        	num=num.substring(1,num.length());
        return num;
    }
}

就像上个栗子一样,我们也画一个图来理解上面的代码:

while循环中的每次我们都会删除一个字符来得到最小值.count计数一旦大于我们要删除的目标数字就停止,然后删除头部的0,得到最后的结果.具体到每次循环的删除哪个字符是根据从左到右删除第一个最大字符来判断.上面栗子的结果也是1219.但是这个版本仍然是错误的.因为删除第一个最大字符这里有问题,举个反例1434589,使用上面的算法计算得到结果为1589,我们以眼算这个栗子的结果应该是1345.可见上面的算法还可以修改,于是又有了第三版错误的代码.

class Solution {
    public String removeKdigits(String num, int k) {
        int len = num.length();
    	if(k>=len)return "0";
    	int count = 0;
    	char indexChar;
    	while(count<k){
    		indexChar = num.charAt(0);
    		for(int i = 1;i < num.length();i++){   			
    			if(num.charAt(i)=='0'&&i==1){
    				indexChar = num.charAt(i-1);
    				break;
    			}
        		if(num.charAt(i)>=indexChar){
        			indexChar = num.charAt(i);      		
        		}else{
        			break;
        		}
    		}
    		num = num.replaceFirst(indexChar+"", "");
    		count++;
    	}
        while(num.startsWith("0")&&num.length()!=1)
        	num=num.substring(1,num.length());
        return num.equals("")?"0":num;
    }
}

基本思路不变,添加了对循环遍历时遇到首位为数字,次位是0的特殊处理,并且修改了找到待删除字符的算法.待删除的点应该为峰值点,即该点的前面都比它小,后面一个比他大,为一个转折点.为了更好理解,再画一个图!

每次删除一定是删除从左到右数第一个最大字符,因为如果删除他前面的小字符,那么大字符左移,结果会变大,如果删除他后面小的字符,那么结果也不是最小.这样看来这次代码没问题了!!!但是还存在一个小小的问题,看下面哦!

无限懵逼ing!这特么一看就完蛋了,这该怎么找错误呢?这么长的字符串,syso都看不到了,控制台了打不完(菜鸡!!!!).难道是源头DP算法也不行了吗???开始重新思考人生QAQ.反复思考发现如果代码在遇到连续'00000'时的处理貌似有问题.既然是每次计算的结果都是最小值.那么为什么要把0开始的字符串处理放在最后呢?于是把第二个while对0开头的处理放到第一个while之中.每次都会正确处理得到正确的结果.代码如下

class Solution {
    public String removeKdigits(String num, int k) {
        int len = num.length();
    	if(k>=len)return "0";
    	int count = 0;
    	char indexChar;
    	while(count<k){
    		indexChar = num.charAt(0);
    		for(int i = 1;i < num.length();i++){   			
    			if(num.charAt(i)=='0'&&i==1){
    				indexChar = num.charAt(i-1);
    				break;
    			}
        		if(num.charAt(i)>=indexChar){
        			indexChar = num.charAt(i);      		
        		}else{
        			break;
        		}
    		}
    		num = num.replaceFirst(indexChar+"", "");
            while(num.startsWith("0")&&num.length()!=1)
        	    num=num.substring(1,num.length());
    		count++;
    	}
        return num.equals("")?"0":num;
    }
}

接下来点击测试!!

稳如老狗!!有没有?哈哈!攻克难关!YES!!!!!!!!!!

接下来是对代码的优化:

	public static String removeKdigits(String num, int k) {
	        int len = num.length();
	    	if(k>=len)return "0";
	    	Stack<Character> stack = new Stack<Character>();
	    	char ch;
	    	int count = 0;
	    	for(int i = 0; i < len;i++){
	    		ch = num.charAt(i);
	    		if(count<k){
	    			if(stack.isEmpty()){
	    				stack.push(ch);
		    		}else if(!stack.isEmpty()&&stack.peek()<=ch){
		    			stack.push(ch);
		    		}else if(!stack.isEmpty()&&stack.peek()>ch){
		    			stack.pop();
		    			count++;
		    			while(!stack.isEmpty()&&stack.peek()>ch&&count<k){
		    				stack.pop();
		    				count++;
		    			}
		    			stack.push(ch);
		    			
		    		}
	    		}else{
	    			stack.push(ch);
	    		}
	    		if(stack.size()==1&&stack.peek()=='0')
	    			stack.pop();
	    	}
	    	while(count<k){
	    		stack.pop();
	    		count++;
	    	}
	    	StringBuilder sb = new StringBuilder();
	    	while(!stack.isEmpty())
	    		sb.append(stack.pop());
	    	if(sb.length()==0)sb.append("0");
	    	return sb.reverse().toString();
	}

内存优化就是不直接使用字符串运算,改用StringBuilder类.运行时间优化 (以最坏的情况来计算) O(1+2+3+......+N+N+N)约等于O(N的二次方),但是最好的情况可能是O(N).

不愧是google的面试题=3=!!但是这个题的难度是medium!!!!!对我来说还是有点难度的呀!!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值