907. 子数组的最小值之和

题目:

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

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

示例 :

输入: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。

提示:

1 <= arr.length <= 3 * 104
1 <= arr[i] <= 3 * 104

问题分析:

输入:   3 1   2   4

left:   -1  -1  1   2       

right:  1   4   4   4   

        最小值是在一段连续数字中被筛选出来的,也就是说每个最小值都有一定的辐射范围,在这个范围内最小值都是同一个。假设给定数组A=[3,1,2,4,1],在一段连续数字3、1、2、4、1中,只要其中一段数字包含1,那么这段数字的最小值肯定为1,例如[3,1,2,4,1]、[3,1,2,4]、[3,1,2]、[1,2]等最小值都为1,我们把这叫做元素1的辐射范围。

        left保存arr[ i ]左边下一个比arr[ i ]小的元素下标,right中保存右边下一个比arr[i]小的元素,这些下标构成了以arr[ i ]为最小值的子数组的边界;

        可以构成arr[i]辐射范围左边界个数为i - left[ i ],可以构成arr[i]辐射范围右边界个数为right[ i ] - i;

        以'1'为例,1的left中有2个左边界{3,1},right中有3个右边界{1,2,4},他们总共可以构成2*3=6个子数组,分别是{1},{3,1},{1,2},{1,2,4},{3,1,2},{3,1,2,4},这些子数组中最小值都是1,那么以'1'辐射的贡献值就是1*(2*3)=6,其他同理,最后将每个元素的贡献值相加即为最终的结果。

代码:

    final private static int MOD = 1000000007;

    public int sumSubarrayMins(int[] arr) {
        if (arr == null || arr.length == 0) {
            return 0;
        }
        int n = arr.length;
        int[] left = new int[n];//left数组中保存左边比arr[i]小的元素的下标
        Deque<Integer> stack = new LinkedList<>();
        int[] right = new int[n];//right数组中保存左边比arr[i]小的元素的下标

        for (int i = 0; i < n; i++) {
            //利用单调栈寻找下一个比arr[i]小的元素
            while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
                stack.pop();
            }
            if (stack.isEmpty()) {
                left[i] = -1;
            } else {
                left[i] = stack.peek();
            }
            stack.push(i);
        }
        stack.clear();
        for (int i = n - 1; i >= 0; i--) {
            while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
                stack.pop();
            }
            if (stack.isEmpty()) {
                right[i] = n;
            } else {
                right[i] = stack.peek();
            }
            stack.push(i);
        }
        long sum = 0;
        for (int i = 0; i < n; i++) {
            sum = (sum + (long) (i - left[i]) * (right[i] - i) * arr[i]) % MOD;
        }
        return (int) sum;
    }

以上是跟着大佬的题解自己跟着分析了一遍,原文链接如下:力扣

下面记录一下一开始写的两个for循环(超时了)

class Solution {
    final private static int MOD = 1000000007;
    public int sumSubarrayMins(int[] arr) {
        if(arr == null || arr.length == 0) return 0;

        long sum = 0;
        //遍历所有子数组情况,将每个子数组最小值相加得到结果
        for(int i = 0;i<arr.length;i++){
            int min = arr[i];
            for(int j = i;j<arr.length;j++){
                min = Math.min(min,arr[j]);
                sum = (sum + min)%MOD;
            }
        }
        return (int)sum;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值