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)
原文地址:https://www.cnblogs.com/h-hkai/p/10691425.html