
思路:单调栈+乘法原理
- 根据示例可以发现直接统计每个arr[i]对答案的贡献会比较快,也就是找arr[i]在哪些区间里为最小值,这就很熟悉了,用单调栈找保证arr[i]最小的左右边界l,r,然后用乘法原理计算其在[l,r]内的子数组数量;
- 左右边界找法:
- l[i]:i左边第一个不大于arr[i]的下标;
- r[i]:i右边第一个不小于arr[i]的下标;
- 为避免重复统计,采用不大于不小于的判别条件。
Java
class Solution {
int MOD = (int)1e9 + 7;
public int sumSubarrayMins(int[] arr) {
int n = arr.length, res = 0;
int[] l = new int[n], r = new int[n];
Arrays.fill(l, -1);
Arrays.fill(r, n);
Deque<Integer> sta = new ArrayDeque<>();
for (int i = 0; i < n; i++) {
while (!sta.isEmpty() && arr[sta.peekLast()] >= arr[i])
r[sta.pollLast()] = i;
sta.addLast(i);
}
sta.clear();
for (int i = n - 1; i >= 0; i--) {
while (!sta.isEmpty() && arr[sta.peekLast()] > arr[i])
l[sta.pollLast()] = i;
sta.addLast(i);
}
for (int i = 0; i < n; i++) {
int left = i - l[i], right = r[i] - i;
res += left * 1L * right % MOD * arr[i] % MOD;
res %= MOD;
}
return res;
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(n)
C++
- stack不能直接
clear()所以用两个栈;
- 忘了
memset()的初始化限制,错了半天……
class Solution {
public:
long long MOD = (int)1e9 + 7;
int sumSubarrayMins(vector<int>& arr) {
int n = arr.size();
long long res = 0;
int l[n], r[n];
memset(l, -1, sizeof(l));
stack<int> lsta,rsta;
for (int i = 0; i < n; i++) {
r[i] = n;
while (!rsta.empty() && arr[rsta.top()] >= arr[i]) {
r[rsta.top()] = i;
rsta.pop();
}
rsta.emplace(i);
}
for (int i = n - 1; i >= 0; i--) {
while (!lsta.empty() && arr[lsta.top()] > arr[i]) {
l[lsta.top()] = i;
lsta.pop();
}
lsta.emplace(i);
}
for (int i = 0; i < n; i++) {
int left = i - l[i], right = r[i] - i;
res += left * 1L * right % MOD * arr[i] % MOD;
res %= MOD;
}
return res;
}
};
- 时间复杂度:O(n)
- 空间复杂度:O(n)
Rust
- 存usize的vec不能初始化为负值,所以左边界整体加1计算,最后答案里减掉。
const MOD:i64 = 1000000007;
impl Solution {
pub fn sum_subarray_mins(arr: Vec<i32>) -> i32 {
let n = arr.len();
let mut res:i64 = 0;
let (mut l, mut r) = (vec![0; n], vec![n; n]);
let mut sta = vec![];
for i in 0..n {
while !sta.is_empty() && arr[sta[sta.len() - 1]] >= arr[i] {
r[sta.pop().unwrap()] = i;
}
sta.push(i);
}
sta.clear();
for i in (0..n).rev() {
while !sta.is_empty() && arr[sta[sta.len() - 1]] > arr[i] {
l[sta.pop().unwrap()] = i + 1;
}
sta.push(i);
}
for i in 0..n {
res += (i - l[i] + 1) as i64 * (r[i] - i) as i64 % MOD * arr[i] as i64 % MOD;
res %= MOD;
}
res as i32
}
}
- 时间复杂度:O(n)
- 空间复杂度:O(n)
总结
- 子区间个数的题,想到用乘法原理计算之后,倒推回去想如何获得两个乘数(合法边界到当前的距离),找最值就用单调栈了,思路清晰!
- 用java的代码改C++和rust,出了各种各样关于初始化的小bug,remind me to pay more attention to details!