LeetCode 每日一题:子数组的最小值之和

50 阅读1分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第 18 天,点击查看活动详情

子数组的最小值之和

原题地址

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

由于答案可能很大,因此 返回答案模 10910^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]。 
最小值为 3124112111,和为 17

示例 2:

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

提示:

  • 1 <= arr.length <= 3 *10410^4
  • 1 <= arr[i] <= 3 *10410^4

思路分析

  1. 首先来理解下题目,题目中要求计算的和的每一项是每个子数组中的最小值,那么我们可以使用单调栈来解决;
  2. 利用单调栈寻找含有每个元素的子数组,并且保证在每个子数组中,该元素都是最小值;然后可以计算每个元素为最小值的子数组有几个,然后记录求和;
  3. 首先遍历数组,定义一个单调递增的栈,用来存放下标,若栈顶元素大于当前元素,则出栈,找到第一个小于当前元素的值,然后为该元素左边界,同理可以找出该元素的右边界;
  4. 然后再遍历一次,找到以当前元素为最小值的所有子数组的个数,然后计算最后的和。

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

END