907. 子数组的最小值之和

180 阅读1分钟

给定一个整数数组 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 * 104
  • 1 <= arr[i] <= 3 * 104

题解:

/**
 * @param {number[]} arr
 * @return {number}
 */
// 方法一:暴力法 超时
var sumSubarrayMins = function (arr) {
    let ans = 0
    for (let i = 0; i < arr.length; i++) {
        let min = arr[i]
        for (let j = i; j < arr.length; j++) {
            min = Math.min(min, arr[j])
            ans = (ans + min) % 1000000007
        }
    }
    return ans
};
// 方法二:单调栈
var sumSubarrayMins = function (arr) {
    const mod = 1e9 + 7
    let stack = []
    // 当前值的左边界
    let left = []
    for (let i = 0; i < arr.length; i++) {
        //这里的 arr[stack[stack.length - 1]] >= arr[i] 大于等于了,
        后面的就只能是大于了,不然会重复计算相等的值
        while (stack.length !== 0 && arr[stack[stack.length - 1]] >= arr[i]) stack.pop()
        left[i] = stack.length ? stack[stack.length - 1] : -1
        stack.push(i)
    }
    stack = []
    // 当前值的右边界
    let right = []
    for (let i = arr.length - 1; i >= 0; i--) {
        while (stack.length !== 0 && arr[stack[stack.length - 1]] > arr[i]) stack.pop()
        right[i] = stack.length ? stack[stack.length - 1] : arr.length
        stack.push(i)
    }

    let sum = 0
    for (let i = 0; i < arr.length; i++) {
        sum += (i - left[i]) * (right[i] - i) * arr[i]
        sum %= mod
    }
    return sum
}

// 方法三:单调栈 + 动态规划
// 例: [3, 1, 2, 4]
// 可以归纳子数组为
// 3 1 + 1 1 + 1 + 2 1 + 1 + 2 + 4
// [3][1][2][4]
// [3, 1][1, 2][2, 4]
// [3, 1, 2][1, 2, 4]
// [3, 1, 2, 4]
// 单调栈初始:stock = [-1][-1, 0][-1, 1][-1, 2][-1, 3]
// 动态数组:[0, 0, 0, 0, 0]
// 总结规律dp[i + 1] = dp[stock[stock.length - 1] + 1] + (i - stock[stock.length - 1] * arr[i]
var sumSubarrayMins = function (arr) {
    let stock = [],
        dp = Array(arr.length + 1).fill(0),
        res = 0,
        m = 1e9 + 7
    stock.push(-1)
    for (let i = 0; i < arr.length; i++) {
        while (stock[stock.length - 1] != -1 && arr[i] <= arr[stock[stock.length - 1]]) {
            stock.pop()
        }
        dp[i + 1] = (dp[stock[stock.length - 1] + 1] + (i - stock[stock.length - 1]) * arr[i]) % m
        stock.push(i)
        res += dp[i + 1]
        res %= m
    }
    return res
};

来源:力扣(LeetCode)

链接:leetcode.cn/problems/su…

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。