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)
  • 相关阅读:
    mac creact-react-app 时包 gyp-error
    JAVA,JDBC报错Access denied for user '"root"'@'localhost
    JAVA,JDBC连接数据库报错:No suitable driver found for "jdbc:mysql://localhost:3306
    JAVA,反射使用demo,通过读取配置文件创建类,调用方法
    JAVA,反射获取class对象的3种方式
    python获取当前时间距离指定时间相差的秒值
    JAVA,字节流文件读写
    JAVA,反射常用方法
    JAVA,字符流读写,flush和close的区别
    JAVA,字符流文件读写
  • 原文地址:https://www.cnblogs.com/h-hkai/p/10691425.html
Copyright © 2011-2022 走看看