Sum of Subarray Minimums
Question:
思路:
这道题的代码很简短, 很容易写. 但是这背后的概念不太好理解. 这题虽然是medium但难度比得上hard
首先我们要打破一个常规思想, 就是找subarray最小值的和. 我们最直接的反应肯定是sliding window, 或者是 brute force, 先找遍所有的subarray, 然后再找到所有的最小值加在一起.
这道题其实有更nb的方法, 但需要我们换个角度想答案, 我们这道题可以这么解. 我们遍历每一个element, 然后找到所有subarray最小element 是当前element的数量. 之后再用当前element * 数量 加到 sum里去。
这样一来就可以实现一遍forloop O(N)
然后我们再思考怎么能找到element 的 subarray数量呢.
我们可以发现我们可以找到一个rang 就是当前element 为中间, 往左找和往右找比当前element 更小的值的index, 我们定义这两个index 为i, j.
那么请问nums[I] ~ nums[j] 是不是就是在这个range中 element是最小的.
for example
2 4 6 7 3 5 1
我们找element 为 3 的 range,
那么往左 i = 0, nums[I] = 2 是 range 的 left bound
往右i = 6, nums[j] = 1 是range 的 right bound
那么3 在range(0, 6) 中间就是最小的值.
ok
那么我们就可以通过
4 ,6, 7 (to the left)
5, 1(to the right)
得到一共在这个range内 包含3 可以有多少个subarray
4, 3, 5
4, 3, 1
6, 3, 5,
6, 3, 1
…
…
一共是 3 * 2 = 6种
不, 还没够
其实还有可能3 的 left 不选 或者3 的right 不选。就是
3, 5
3, 1
4,3
6, 3
7, 3
3
那么就相当于
_,4 ,6, 7 (to the left)
_,5, 1(to the right)
一共是 4 * 3 = 12 个 subarray
那么我们就能算到, 3为最小subarray的数量是12个.
然后我们就可以3*12 = 36.
那么这个是找的3的.
我们用同样的方法找全部element的 subarray数量.
用monotic increasing stack, 当insert element的时候把stack里比insert的element更大的element pop 出去. 此时pop出去的element我们就能得知, 它的下一个smaller element 就是我们此时正在insert的number, 而它的左边一个smaller的number恰好就是stack里下面的那一个element
那么我们找到了pop出来的这个element的左边下一个smaller和右边下一个smaller. 那就可以得到range了
ye!!!
如果pop出来的element是最后一个element, 接着stack就空了, 那么这个element 左边的bound 就是index = -1. 没问题的.
具体monotic stack里边的细节 看这道题在leetcode 的soluiton.
class Solution {
public int sumSubarrayMins(int[] arr) {
int MOD = 1000000007;
Stack<Integer> stack = new Stack<>();
long sumOfMinimums = 0;
// building monotonically increasing stack
for (int i = 0; i <= arr.length; i++) {
// when i reaches the array length, it is an indication that
// all the elements have been processed, and the remaining
// elements in the stack should now be popped out.
while (!stack.empty() && (i == arr.length || arr[stack.peek()] >= arr[i])) {
// Notice the sign ">=", This ensures that no contribution
// is counted twice. rightBoundary takes equal or smaller
// elements into account while leftBoundary takes only the
// strictly smaller elements into account
int mid = stack.pop();
int leftBoundary = stack.empty() ? -1 : stack.peek();
int rightBoundary = i;
// count of subarrays where mid is the minimum element
long count = (mid - leftBoundary) * (rightBoundary - mid) % MOD;
sumOfMinimums += (count * arr[mid]) % MOD;
sumOfMinimums %= MOD;
}
stack.push(i);
}
return (int) (sumOfMinimums);
}
}