zoukankan      html  css  js  c++  java
  • 907. Sum of Subarray Minimums

    Given an array of integers A, find the sum of min(B), where B ranges over every (contiguous) subarray of A.

    Since the answer may be large, return the answer modulo 10^9 + 7.

    Example 1:

    Input: [3,1,2,4]
    Output: 17
    Explanation: Subarrays are [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], [1,2,4], [3,1,2,4]. 
    Minimums are 3, 1, 2, 4, 1, 1, 2, 1, 1, 1.  Sum is 17.

    Note:

    1. 1 <= A.length <= 30000
    2. 1 <= A[i] <= 30000

    Approach #1: Monotone Stack. [Java]

    class Solution {
        public int sumSubarrayMins(int[] A) {
            int n = A.length;
            Stack<int[]> in_stk_p = new Stack<>(), in_stk_n = new Stack<>();
            // left is for the distance to previous less element
            // right is for the distance to next less element.
            int[] left = new int[n], right = new int[n];
            for (int i = 0; i < n; ++i) left[i] = i + 1;
            for (int i = 0; i < n; ++i) right[i] = n - i;
            
            for (int i = 0; i < n; ++i) {
                while (!in_stk_p.isEmpty() && in_stk_p.peek()[0] > A[i]) in_stk_p.pop();
                left[i] = in_stk_p.isEmpty() ? i + 1 : i - in_stk_p.peek()[1];
                in_stk_p.push(new int[] {A[i], i});
                
                while (!in_stk_n.isEmpty() && in_stk_n.peek()[0] > A[i]) {
                    int[] x = in_stk_n.peek();
                    in_stk_n.pop();
                    right[x[1]] = i - x[1];
                }
                in_stk_n.push(new int[] {A[i], i});
            }
            
            int res = 0, mod = (int)1e9 + 7;
            for (int i = 0; i < n; ++i) 
                res = (res + A[i]*left[i]*right[i]) % mod;
            
            return res;
        }
    }
    

      

    Analysis:

    Before diving into the solution, we first introduce a very important stack type, which is called momotone stack.

    What is monotonous increase stack?

    Roughly spkeaking, the elements in the an monotonous increase stack keeps an increasing order.

    The typical paradigm for monotonous increase stack:

    for(int i = 0; i < A.size(); i++){
      while(!in_stk.empty() && in_stk.top() > A[i]){
        in_stk.pop();
      }
      in_stk.push(A[i]);
    }.
    

      

    What can monotonous increase stack do?

    (1) find the previous less element of each element in a vector with O(n) time:

    What is the previous less element of an element?

    For example:

    [3, 7, 8, 4]

    The previous less element of 7 is 3.

    The previous less element of 8 is 7.

    The previous less element of 4 is 3.

    There are no previous less element for 3.

    For simplicity of notation, we use abbreviation PLE to denote Previous Less Element.

    C++ code (by slitghly modifying the paradigm):

    Instead of directly pushing the element itself, here for simplicity, we push the incex.

    We do some record when the index is pushed into the stack.

    // previous_less[i] = j means A[j] is the previous less element of A[i].

    // previous_less[i] = -1 means there is no previous less element of A[i].

    vector<int> previous_less(A.size(), -1);
    for(int i = 0; i < A.size(); i++){
      while(!in_stk.empty() && A[in_stk.top()] > A[i]){
        in_stk.pop();
      }
      previous_less[i] = in_stk.empty()? -1: in_stk.top();
      in_stk.push(i);
    }
    

      

    (2) find the next less element of each element in a vector with O(n) time:

    What is the next less element of an element?

    For example:

    [3, 7, 8, 4]

    The next less element of 8 is 4.

    The next less element of 7 is 4.

    There is no next less element for 3 and 4.

    For simplicity of notation, we use abbreviation NLE to denote Next Less Element.

    C++ code (by slighly modifying the paradigm):

    We do some record when the index is poped out from the stack.

    // next_less[i] = j means A[j] is the next less element of A[i].

    // next_less[i] = -1 mean there is no next less element of A[i].

    vector<int> previous_less(A.size(), -1);
    for(int i = 0; i < A.size(); i++){
      while(!in_stk.empty() && A[in_stk.top()] > A[i]){
        auto x = in_stk.top(); in_stk.pop();
        next_less[x] = i;
      }
      in_stk.push(i);
    }
    

      

    How can the monotonous increase stack be applied to this problem?

    For example:

    Consider the element 3 in the following vector:

            [2, 9, 7, 8, 3, 4, 6, 1]

             |         |

        the previous less    the next less 

          element of 3       element of 3

    After finding both NLE and PLE of 3, we can determine the distance between 3 and 2(prevous less), and the distance between 3 and 1(next less). In this example, the distance is 4 and 3 respectively.

    How many subarray with 3 being its minimum value?

    The answer is 4 * 3.

    How much the element 3 contributes to the final answer?

    It is 3 * (3 * 4).

    What is the final answer?

    Denote  by left[i] the distance between element A[i] and its PLE.

    Denote by right[i] the distance beween element A[i] and its NLE.

    The final answer is:

    sum(A[i] * left[i] * right[i])

    Approach #2: Optimize [C++]

    class Solution {
    public:
        int sumSubarrayMins(vector<int>& A) {
            int res = 0, n = A.size(), mod = 1e9 + 7, j, k;
            stack<int> s;
            for (int i = 0; i <= n; ++i) {
                while (!s.empty() && A[s.top()] > (i == n ? 0 : A[i])) {
                    j = s.top(), s.pop();
                    k = s.empty() ? -1 : s.top();
                    res = (res + A[j] * (i - j) * (j - k)) % mod;
                }
                s.push(i);
            }
            return res;
        }
    };
    

      

    Analysis:

    1. Here we record (A[i], i) in the stack. We can also only record index.

    2. For left part and right part, the logic is same. 

    So for each, we used one stack and one pass.

    This process can be optimized to one pass using one stack in total.

    Reference:

    https://leetcode.com/problems/sum-of-subarray-minimums/discuss/170750/C%2B%2BJavaPython-Stack-Solution

    https://docs.oracle.com/javase/7/docs/api/java/util/Stack.html

    https://leetcode.com/problems/sum-of-subarray-minimums/discuss/178876/stack-solution-with-very-detailed-explanation-step-by-step

    永远渴望,大智若愚(stay hungry, stay foolish)
  • 相关阅读:
    6、CC2541修改按键调节广播发送功率例程为持续发送4DB的蓝牙基站
    [nRF51822] 16、nRF51822的随机数生成器,及随机数生成器的一些知识(可以帮您补补随机数发生器的知识)
    [PCB设计] 4、BAT脚本处理AD生成的GERBER文件为生产文件
    [异常解决] 奇巧淫技——VirtualBox中的linux无显示启动,并在win7上远程控制
    [PCB设计] 3、用CAM350修改GERBER文件(删除某些部分)
    [异常解决] Make nRF51 DFU Project Appear "fatal error: uECC.h: No such file or directory"
    [异常解决] How to build a gcc toolchain for nRF51 on linux (very detailed!!!)
    [异常解决] windows用SSH和linux同步文件&linux开启SSH&ssh client 报 algorithm negotiation failed的解决方法之一
    [模拟电路] 2、Passive Band Pass Filter
    Docker常用命令
  • 原文地址:https://www.cnblogs.com/h-hkai/p/10691425.html
Copyright © 2011-2022 走看看