持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 18 天,点击查看活动详情。
子数组的最小值之和
原题地址
给定一个整数数组 arr
,找到 min(b)
的总和,其中 b
的范围为 arr
的每个(连续)子数组。
由于答案可能很大,因此 返回答案模 + 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 *
1 <= arr[i] <= 3 *
思路分析
- 首先来理解下题目,题目中要求计算的和的每一项是每个子数组中的最小值,那么我们可以使用单调栈来解决;
- 利用单调栈寻找含有每个元素的子数组,并且保证在每个子数组中,该元素都是最小值;然后可以计算每个元素为最小值的子数组有几个,然后记录求和;
- 首先遍历数组,定义一个单调递增的栈,用来存放下标,若栈顶元素大于当前元素,则出栈,找到第一个小于当前元素的值,然后为该元素左边界,同理可以找出该元素的右边界;
- 然后再遍历一次,找到以当前元素为最小值的所有子数组的个数,然后计算最后的和。
AC 代码
/**
* @param {number[]} arr
* @return {number}
*/
var sumSubarrayMins = function(arr) {
// 维护一个严格单调递增的栈,栈中存下标
const strictStack = []
const left = []
const right = []
const len = arr.length
const mod = 1e9 + 7
let res = 0
for(let i = 0; i < len; i++) {
while(strictStack.length && arr[strictStack[strictStack.length - 1]] >= arr[i]) {
strictStack.pop()
}
const top = strictStack[strictStack.length - 1] ?? -1
strictStack.push(i)
left.push(top + 1)
}
strictStack.length = 0
for(let i = len - 1; i >= 0; i--) {
while(strictStack.length && arr[strictStack[strictStack.length - 1]] > arr[i]) {
strictStack.pop()
}
const top = strictStack[strictStack.length - 1] ?? len
strictStack.push(i)
right.push(top - 1)
}
const rightLen = right.length
for(let i = 0; i < len; i++) {
const subLen = (i - left[i] + 1) * (right[rightLen - i - 1] - i + 1)
const help = subLen % mod * arr[i] % mod
res = (res + help) % mod
}
return res
};
结果:
- 执行结果: 通过
- 执行用时:84 ms, 在所有 JavaScript 提交中击败了88.51%的用户
- 内存消耗:53.9 MB, 在所有 JavaScript 提交中击败了12.16%的用户
- 通过测试用例:87 / 87