题目

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

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

示例 1:

输入:arr = [3,1,2,4]
输出:17
解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17。

示例 2:

输入:arr = [11,81,94,43,3]
输出:444

提示:

1 <= arr.length <= 3 10^4
1 <= arr[i] <= 3
10^4

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

思路

2281的前置题,这个题倒是会做,使用单调栈找出一个数左边和右边第一个比自己小或者大的数,这类题做过不少,比如2104题。

计算完leftFirstLow和rightFirstLow(定义见注释)后,对于一个数arr[i],其可以作为907. 子数组的最小值之和 - 图1%20*%20(rightFirstLow%5Bi%5D%20-%20i)#card=math&code=%28i%20-%20leftFirstLow%5Bi%5D%29%20%2A%20%28rightFirstLow%5Bi%5D%20-%20i%29&id=Ml8MD)个子数组的最小值,乘以arr[i]累加到ans中。

代码

代码一

  1. class Solution {
  2. public int sumSubarrayMins(int[] arr) {
  3. int n = arr.length;
  4. Deque<Integer> stack = new ArrayDeque<>();
  5. // leftFirstLow[i]表示nums[i]左边最近的 "<=nums[i]" 的元素的下标
  6. int[] leftFirstLow = new int[n];
  7. // rightFirstLow[i]表示nums[i]右边最近的 "<nums[i]" 的元素的下标
  8. int[] rightFirstLow = new int[n];
  9. for (int i = 0; i <= n; i++) {
  10. int num = i == n ? Integer.MIN_VALUE : arr[i];
  11. while (!stack.isEmpty() && num < arr[stack.peek()]) {
  12. int k = stack.pop();
  13. leftFirstLow[k] = stack.isEmpty() ? -1 : stack.peek();
  14. rightFirstLow[k] = i;
  15. }
  16. stack.push(i);
  17. }
  18. long ans = 0;
  19. int mod = (int) (1e9 + 7);
  20. for (int i = 0; i < n; i++) {
  21. // 注意三个数相乘后可能会int溢出,因此
  22. ans = (ans + (long) arr[i] * (i - leftFirstLow[i]) * (rightFirstLow[i] - i)) % mod;
  23. }
  24. return (int) ans;
  25. }
  26. }

代码二

因为leftFirstLow和rightFirstLow可以一起算出来,所以没必要将这两个数组算出来最后再加,可以直接在循环中计算完leftFirstLow[k]和rightFirstLow[k]后就将k处的贡献累加到ans中。

  1. class Solution {
  2. public int sumSubarrayMins(int[] arr) {
  3. int n = arr.length;
  4. Deque<Integer> stack = new ArrayDeque<>();
  5. long ans = 0;
  6. int mod = (int) (1e9 + 7);
  7. for (int i = 0; i <= n; i++) {
  8. int num = i == n ? Integer.MIN_VALUE : arr[i];
  9. while (!stack.isEmpty() && num < arr[stack.peek()]) {
  10. int k = stack.pop();
  11. int leftFirstLow = stack.isEmpty() ? -1 : stack.peek();
  12. ans = (ans + (long) arr[k] * (k - leftFirstLow) * (i - k)) % mod;
  13. }
  14. stack.push(i);
  15. }
  16. return (int) ans;
  17. }
  18. }