其他分享
首页 > 其他分享> > [LeetCode] 907. Sum of Subarray Minimums

[LeetCode] 907. Sum of Subarray Minimums

作者:互联网

Given an array of integers arr, find the sum of min(b), where b ranges over every (contiguous) subarray of arr. Since the answer may be large, return the answer modulo 109 + 7.

Example 1:

Input: arr = [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.

Example 2:

Input: arr = [11,81,94,43,3]
Output: 444

Constraints:

子数组的最小值之和。

给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。

由于答案可能很大,因此 返回答案模 10^9 + 7 。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/sum-of-subarray-minimums
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题目给的是数组,要我们求的东西也很简单,就是找出 input 数组里所有的子数组,并且把每个子数组里的最小值拿出来,计算所有最小值的和。

暴力解不难想,可以手动找出所有的子数组,然后找每个子数组的最小值。但是介于题目的数据范围会到 10 的 4 次方,暴力解会超时。我直接介绍最优解单调栈。我参考了这个帖子

单调栈的特点就是可以在 O(n) 的时间内找到数组里的一个拐点。如下代码,是一个单调递增栈的代码。当我们遍历到 A[i] 的时候,如果 A[i] 小于栈顶元素,那么我们就一直把栈顶元素弹出。弹出操作完毕之后,再放入 A[i]。这个操作我们很熟悉,但是对于这道题,单调栈巧妙的地方在于当我们从 while 循环跳出来之后,也就正好找到了包含 A[i] 的子数组的左边界,且 A[i] 是这个子数组的最小值。

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

我们通过这个方式可以找到所有包含 A[i] 且 A[i] 是最小值的子数组的左边界,我们同时也要找到右边界,才能确定整个子数组的长度。所以我们需要创建两个和 input 数组等长的数组,一个记录每个数字 A[i] 的左边界 left[i],一个记录右边界 right[i]。最后结算的时候,对于每个数字 A[i],以 A[i] 为最小值可以形成的子数组的数量 = i 到左边界的距离 * i 到右边界的距离。

最后需要提醒的是,每个数字 A[i] 都有可能是某个非空子数组的最小值,比如长度为 1 的子数组,只包含 A[i] 自身的子数组,A[i] 自然就是这个子数组的最小值。单调栈的做法真的很巧妙。

时间O(n)

空间O(n)

Java实现

 1 class Solution {
 2     private int MOD = (int) Math.pow(10, 9) + 7;
 3 
 4     public int sumSubarrayMins(int[] arr) {
 5         // corner case
 6         if (arr == null || arr.length == 0) {
 7             return 0;
 8         }
 9 
10         // normal case
11         int len = arr.length;
12         int[] left = new int[len];
13         int[] right = new int[len];
14         Deque<Integer> stack = new ArrayDeque<>();
15         for (int i = 0; i < len; i++) {
16             while (!stack.isEmpty() && arr[i] < arr[stack.peek()]) {
17                 stack.pop();
18             }
19             if (stack.isEmpty()) {
20                 left[i] = -1;
21             } else {
22                 left[i] = stack.peek();
23             }
24             stack.push(i);
25         }
26 
27         stack.clear();
28         for (int i = len - 1; i >= 0; i--) {
29             // 需要有一边是带等号的,才不会漏解
30             while (!stack.isEmpty() && arr[i] <= arr[stack.peek()]) {
31                 stack.pop();
32             }
33             if (stack.isEmpty()) {
34                 right[i] = len;
35             } else {
36                 right[i] = stack.peek();
37             }
38             stack.push(i);
39         }
40 
41         long res = 0;
42         for (int i = 0; i < len; i++) {
43             res = (res + (long) (i - left[i]) * (right[i] - i) * arr[i]) % MOD;
44         }
45         return (int) res;
46     }
47 }

 

LeetCode 题目总结

标签:arr,Minimums,int,Sum,len,最小值,数组,Subarray,stack
来源: https://www.cnblogs.com/cnoodle/p/16463255.html