leetCode每日十题---栈(难度:中等)三

题目描述1

在这里插入图片描述

笔者解答1.1

class Solution {
    
    
    public int scoreOfParentheses(String S) {
    
    
          Stack<Integer> stack=new Stack<Integer>();
          //-1代表左括号
          for(int i=0;i<S.length();i++){
    
    
              if(stack.isEmpty()||S.charAt(i)=='('){
    
    
                  stack.push(-1);
              }else{
    
    
                    if(stack.peek()==-1){
    
    
                      stack.pop();
                      if(stack.isEmpty()||stack.peek()==-1)
                        stack.push(1);
                      else{
    
    
                        stack.push(1+stack.pop());
                      }
                    }else{
    
    
                      int temp=stack.pop()*2;
                      stack.pop();
                      if(stack.isEmpty()||stack.peek()==-1)
                       stack.push(temp);
                       else
                       stack.push(temp+stack.pop());
                    }                
              }
          }
          return stack.pop();
    }
}

笔者分析1.2

虽然是个基础题,但写出来还是略有成就感的,重在思考过程。刚开始是打算用两个栈,一个栈用来记录括号,一个用来记录分数,但最后发现,两个栈向互受到牵制,所以必须合成一个栈,考虑到分数是整数,符号是字符型,要想合成一个栈,则可考虑将符号用特殊数字代替,显然这里的分数都是正的,所以用-1来表示左括号。遍历字符串,若为左括号则直接入栈,若为右括号,则判定栈顶是正数还是负数,若为正数,则将其扩大两倍再存入。若为负数,则弹栈,存入1,这里需要注意,存入1之前,应该先判断栈内的情况,若为正数则二者相加,若为负数则直接入栈。

题目描述2

在这里插入图片描述

笔者解答2.2

class Solution {
    
    
    public String decodeAtIndex(String S, int K) {
    
    
        int lastlength=0;
        int alllength=0;
        String str="";
       for(int i=0;i<S.length();i++){
    
    
         if(S.charAt(i)<='9'&&S.charAt(i)>='0'){
    
    
             alllength=(S.charAt(i)-'0')*lastlength;
             int num=S.charAt(i)-'0';
             String temp_str=str;
             str="";
             while(num>=1){
    
    
                 str+=temp_str;
                 num--;
             }
             if(alllength>=K){
    
    
                 return ""+str.charAt((K-1)%lastlength);
             }
             lastlength=alllength;
         }else{
    
    
             lastlength++;
             str=str+""+S.charAt(i);
              if(lastlength>=K){
    
    
                 return ""+str.charAt((K-1)%lastlength);
             }
         }
       }
       return S.charAt(K-1)+"";
    }
}

笔者分析2.2

这题写起来很顺利,但通过率只有1/5,刚开始我很是纳闷,直到我的代码运行超出内存限制。。。打脸了打脸了。只得求助评论区。
逆向工作法
如果我们有一个像 appleappleappleappleappleapple 这样的解码字符串和一个像 K=24 这样的索引,那么如果 K=4,答案是相同的。
一般来说,当解码的字符串等于某个长度为 size 的单词重复某些次数(例如 apple 与 size=5 组合重复6次)时,索引 K 的答案与索引 K % size 的答案相同。
我们可以通过逆向工作,跟踪解码字符串的大小来使用这种洞察力。每当解码的字符串等于某些单词 word 重复 d 次时,我们就可以将 k 减少到 K % (Word.Length)。
说实话,这思路和我一样嘛,为啥我就超出内存限制呢。主要还是str惹的祸,其实没必要每次存储字符串。不过,这逆向思维还是挺厉害的。

class Solution {
    
    
    public String decodeAtIndex(String S, int K) {
    
    
        long size = 0;
        int N = S.length();

        // Find size = length of decoded string
        for (int i = 0; i < N; ++i) {
    
    
            char c = S.charAt(i);
            if (Character.isDigit(c))
                size *= c - '0';
            else
                size++;
        }
        for (int i = N-1; i >= 0; --i) {
    
    
            char c = S.charAt(i);
            K %= size;
            if (K == 0 && Character.isLetter(c))
                return Character.toString(c);
            if (Character.isDigit(c))
                size /= c - '0';
            else
                size--;
        }

        throw null;
    }
}

题目描述3

在这里插入图片描述

笔者解答3.1

class Solution {
    
    
    public int sumSubarrayMins(int[] A) {
    
    
        int sum=0;
        for(int i=0;i<A.length;i++){
    
    
            int min=A[i];
            for(int j=i;j<A.length;j++){
    
    
                if(A[j]<=min)
                    min=A[j];
                sum=(sum+min)%(1000000000+7);              
            }
        }
        return sum;
      }
}

笔者分析3.2

我当然知道不能这么写,一是这思路太普通了,而且肯定超时,但我是真没想出其它方法啊。虽然有个比较好的思路,就是计算每个数再子数组中最小的次数,但没有想出如何实现,不过评论区有人实现了,非常简洁。

class Solution {
    
    
    public int sumSubarrayMins(int[] A) {
    
    
        long res = 0;
        long mod = 1000000007;
        for (int i = 0; i<A.length; i++) {
    
    
            int l = i-1;
            for (; l>=0 && A[i] < A[l]; l--) ;
            int r = i+1;
            for (; r<A.length && A[i] <= A[r]; r++) ;
            
            res += (i-l)*(r-i)*A[i];
        }
        return (int)(res % mod);
    }
}

题目描述4

在这里插入图片描述

笔者解答4.1

class StockSpanner {
    
    
    Stack<Integer> stack;
    Stack<Integer> stack_score;
    public StockSpanner() {
    
    
    stack=new Stack<Integer>();
    stack_score=new Stack<Integer>();
    } 
    public int next(int price) {
    
    
        if(stack.isEmpty()||stack.peek()>price){
    
    
            stack.push(price);
            stack_score.push(1);
            return 1;
        }else{
    
    
            int temp_score=1;
            while(!stack.isEmpty()&&price>=stack.peek()){
    
    
                stack.pop();
                temp_score+=stack_score.pop();
            }
            stack.push(price);
            stack_score.push(temp_score);
            return temp_score;
        }
    }
}

笔者分析4.2

也算是挽回点颜面,在比较客观地执行用时和内存消耗内完成了这题,主要是这题简单,跟我没啥关系。。。用了两个栈,因为以前的数据要保留,而且要想提高效率,还得用另一个栈保存每个点的得分。主体思想还是单调栈,保证栈内不增即可。有想过用其它数据结构,但发现还是栈比较合适。

题目描述5

在这里插入图片描述

笔者解答5.1

class Solution {
    
    
public int longestWPI(int[] hours) {
    
    
        int n = hours.length;
        for(int i = 0; i < n; i++){
    
    
            hours[i] = hours[i] > 8 ? 1 : -1;
        }
        int res = 0;
        for(int i = 0; i < n; i++){
    
    
            int count = 0;
            for(int j = i; j < n; j++){
    
    
                count += hours[j];
                if(count > 0)
                    res = Math.max(res, j - i + 1);
            }
            if(n - i <= res)
                return res;
        }
        return res;
    }
}

笔者分析5.2

说实话这题我没有想到什么好的办法,和暴力解没什么区别,很意外的是它竟然没有判我超时,也许有可取之处吧。大致思路是,这里的数据值大小反应的是两种状态,数值本身没有意义,所以我们可以统一的用1和-1来表示两种状态,而 依题目意思,就成了在一组0,1序列中,找出最长的一段,该段的和大于0。

题目描述6

在这里插入图片描述

笔者解答6.1

class Solution {
    
    
    public String minRemoveToMakeValid(String s) {
    
    
        Stack<Integer> stack=new Stack<Integer>();
        int i;
        for(i=0;i<s.length();i++){
    
    
           if(stack.isEmpty()){
    
    
               if(s.charAt(i)=='(')
                 stack.push(-1-i);
                else if(s.charAt(i)==')'){
    
    
                    stack.push(i);
                }
           }else{
    
    
               if(stack.peek()<0)
               {
    
    
               if(s.charAt(i)=='(')
                  stack.push(-1-i);
                else if(s.charAt(i)==')')
                  stack.pop();
               }
               else{
    
    
                 if(s.charAt(i)=='(')
                  stack.push(-1-i);
                else if(s.charAt(i)==')')
                  stack.push(i);   
               }
           }
        }
        if(stack.isEmpty())
        return s;
        int[] num=new int[s.length()];
        int length=0;
        while(!stack.isEmpty()){
    
    
           if(stack.peek()>=0)
            num[length]=stack.pop();
            else
            num[length]=-1-stack.pop();
            length++;
        }
        String str="";
        for(i=0;i<s.length();i++){
    
    
           if(length>0&&i==num[length-1]){
    
    
                length--; 
           } else{
    
    
            str+=s.charAt(i)+"";
           }
        }
        return str;
    }
}

笔者分析6.2

虽然是写出来了,但执行时间和内存花销都很大,期间还调整了很多bug,反正就很烂吧。只不过想到了一个方法来在一个栈中既保存左括号和右括号,又保存它们的位置索引,就是用正负来区分是左括号还是右括号,绝对值表示它们的位置,这里特别强调索引为0的位置,如果只用正负的话这个位置是无法确定是左括号还是右括号的。所以在上述代码中,我用了-1代替0,而且它的索引值也不再是绝对值了,需要用-1来减。

总结

栈的中等难度三十题完结了,明天开始困难难度,每日十题打卡第四天,下图为证。
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/Achenming1314/article/details/108314650