持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第34天,点击查看活动详情
给定一个整数数组 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
思路
本题可以使用暴力解题,就是枚举所有可能的子数组,然后遍历子数组找到最小值,再求和。这种解法虽然好理解,但是浪费空间可时间,不推荐。
本题还可以使用单调栈求解。设arr
的长度为n
,arr
的子数组最小值和为sum
,对于arr[i](0 <= i < n)
,我们需要计算它对sum
的贡献值,即arr[i]
在多少个子数组中为最小值,我们可以分别向左和向右遍历,分别找到第一个大于等于arr[i]
的值arr[left]
和arr[right]
,所有子数组个数为count = (idx - left +1) * (Math.max(right - idx), 1)
,贡献值为arr[idx] * count
。定义stack为单调递减栈,遍历arr
,维护一个单调递减的栈,入栈的时候可以计算left
的值,但是无法计算right
值,故我们在出栈的时候计算left
和right
,为了统一处理arr[0]
和arr[n-1]
,我们在arr
头和尾分别添加一个0
元素,代码如下。
解题
/**
* @param {number[]} arr
* @return {number}
*/
var sumSubarrayMins = function (arr) {
const MOD = Math.pow(10, 9) + 7;
arr.unshift(0);
arr.push(0);
const n = arr.length;
const idxs = [0];
let sum = 0;
for (let i = 1; i < n; i++) {
while (arr[idxs.at(-1)] >= arr[i]) {
const idx = idxs.pop();
const right = i - idx;
const left = idxs.length ? idx - idxs.at(-1) : 1;
sum = (sum + right * left * arr[idx]) % MOD;
}
idxs.push(i);
}
return sum;
};