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)
  • 相关阅读:
    Python中 sys.argv[]的用法简明解释
    Python--文件操作和集合
    Python--各种参数类型
    Python-- 字符串格式化 (%操作符)
    Python--绝对路径和相对路径
    Python3.x和Python2.x的区别
    关于ArrayList使用中的注意点
    Sun公司建议的Java类定义模板
    SWT组件之Table浅析
    mysql的相关命令整理(二)
  • 原文地址:https://www.cnblogs.com/h-hkai/p/10691425.html
Copyright © 2011-2022 走看看