Medium
题目描述
给定一个整数数组 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 * 10^41 <= arr[i] <= 3 * 10^4
解题思路
这道题需要计算所有子数组的最小值之和。暴力解法是枚举所有子数组,时间复杂度为 O(n³),会超时。
核心思路:对于每个元素 arr[i],计算它作为最小值的子数组数量,然后乘以 arr[i] 得到贡献值。
关键在于找到每个元素的「影响范围」:
- 左边界:找到左侧第一个小于当前元素的位置
- 右边界:找到右侧第一个小于等于当前元素的位置
使用单调递增栈来高效求解:
- 遍历数组,维护单调递增栈
- 当遇到更小元素时,栈顶元素找到了右边界
- 栈中下一个元素就是左边界
- 计算贡献:
arr[i] * (i - left) * (right - i)
注意事项:
- 为避免重复计算,左边界用"严格小于",右边界用"小于等于"
- 需要处理边界情况(栈为空时的边界)
- 结果要对
10^9 + 7取余
这种方法时间复杂度为 O(n),空间复杂度为 O(n),是最优解法。
代码实现
class Solution {
public:
int sumSubarrayMins(vector<int>& arr) {
const int MOD = 1e9 + 7;
int n = arr.size();
vector<int> left(n), right(n);
stack<int> st;
// 计算左边界:左侧第一个小于当前元素的位置
for (int i = 0; i < n; i++) {
while (!st.empty() && arr[st.top()] >= arr[i]) {
st.pop();
}
left[i] = st.empty() ? -1 : st.top();
st.push(i);
}
// 清空栈,计算右边界:右侧第一个小于等于当前元素的位置
while (!st.empty()) st.pop();
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && arr[st.top()] > arr[i]) {
st.pop();
}
right[i] = st.empty() ? n : st.top();
st.push(i);
}
long long result = 0;
for (int i = 0; i < n; i++) {
long long contribution = (long long)arr[i] * (i - left[i]) * (right[i] - i);
result = (result + contribution) % MOD;
}
return result;
}
};
class Solution:
def sumSubarrayMins(self, arr: List[int]) -> int:
MOD = 10**9 + 7
n = len(arr)
left = [0] * n
right = [0] * n
stack = []
# 计算左边界:左侧第一个小于当前元素的位置
for i in range(n):
while stack and arr[stack[-1]] >= arr[i]:
stack.pop()
left[i] = -1 if not stack else stack[-1]
stack.append(i)
# 清空栈,计算右边界:右侧第一个小于等于当前元素的位置
stack.clear()
for i in range(n - 1, -1, -1):
while stack and arr[stack[-1]] > arr[i]:
stack.pop()
right[i] = n if not stack else stack[-1]
stack.append(i)
result = 0
for i in range(n):
contribution = arr[i] * (i - left[i]) * (right[i] - i)
result = (result + contribution) % MOD
return result
public class Solution {
public int SumSubarrayMins(int[] arr) {
const int MOD = 1000000007;
int n = arr.Length;
int[] left = new int[n];
int[] right = new int[n];
Stack<int> stack = new Stack<int>();
// 计算左边界:左侧第一个小于当前元素的位置
for (int i = 0; i < n; i++) {
while (stack.Count > 0 && arr[stack.Peek()] >= arr[i]) {
stack.Pop();
}
left[i] = stack.Count == 0 ? -1 : stack.Peek();
stack.Push(i);
}
// 清空栈,计算右边界:右侧第一个小于等于当前元素的位置
stack.Clear();
for (int i = n - 1; i >= 0; i--) {
while (stack.Count > 0 && arr[stack.Peek()] > arr[i]) {
stack.Pop();
}
right[i] = stack.Count == 0 ? n : stack.Peek();
stack.Push(i);
}
long result = 0;
for (int i = 0; i < n; i++) {
long contribution = (long)arr[i] * (i - left[i]) * (right[i] - i);
result = (result + contribution) % MOD;
}
return (int)result;
}
}
var sumSubarrayMins = function(arr) {
const MOD = 1e9 + 7;
const n = arr.length;
// Find previous less element for each index
const prevLess = new Array(n).fill(-1);
const stack1 = [];
for (let i = 0; i < n; i++) {
while (stack1.length && arr[stack1[stack1.length - 1]] > arr[i]) {
stack1.pop();
}
if (stack1.length) {
prevLess[i] = stack1[stack1.length - 1];
}
stack1.push(i);
}
// Find next less or equal element for each index
const nextLessEqual = new Array(n).fill(n);
const stack2 = [];
for (let i = n - 1; i >= 0; i--) {
while (stack2.length && arr[stack2[stack2.length - 1]] >= arr[i]) {
stack2.pop();
}
if (stack2.length) {
nextLessEqual[i] = stack2[stack2.length - 1];
}
stack2.push(i);
}
let result = 0;
for (let i = 0; i < n; i++) {
const left = i - prevLess[i];
const right = nextLessEqual[i] - i;
result = (result + arr[i] * left * right) % MOD;
}
return result;
};
复杂度分析
| 复杂度类型 | 值 | 说明 |
|---|---|---|
| 时间复杂度 | O(n) | 每个元素最多进栈出栈一次,总体为线性时间 |
| 空间复杂度 | O(n) | 需要额外的数组存储左右边界和栈空间 |
相关题目
. Sum of Subarray Ranges (Medium)