题目:
给定一个整数数组 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 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