- No, it's clear from problem description.
Input:
Output:
Input:
Output:
Idea! We will find the smallest number and see how many time does the smallest number contribute to the sum of min(b)
in the subarray.
Suppose we have a smallest number 2
in our array X X X X 2 X X X
, we will find out that how many time does 2
contribute to the sum of min(b)
. We can keep looking at the left and right hand side until we find the number that is smaller than 2
.
// Search the smaller number from left and right hand side
X X X X 2 X X X
<-----*----->
// This is the range that the subarray contains 2 and contributes the min(b)
X X 0 3 2 4 5 1
|-*---|
We will keep looking for the previous and next smaller numbers. (prevSmaller
, nextSmaller
).
Then 2
contributes to the sum of min(b)
from all the subarrays ranging from [3, 2, 4, 5]
that contains 2
, that is all the subarrays begin from [3, 2]
, end at [2, 4, 5]
, so there are 2 * 3 subarrays.
/// The possible start of subarray that contains 2.
[3, 2, _, _]
/// The possible end of subarray that contains 2.
[_, 2, 4, 5]
- What if we can't find the previous or next smaller number? We have to set
-1
orn
to indicate that there is no such number.
index -1, 0, 1, 2, 3, 4, 5, 6, 7
value 3 5 1 2 4 5 9
|--------*--------------|
- What if we have the same smallest number?
... 2 3 2 4 5 2 ...
|---*-----|
There are duplicate 2
(leftmost and rightmost), which one contributes to min(b)
in ..., 2 3 2 4 5 2, ...
? To prevent duplicate calculation, we treat the first 2
the minimum number and ignore other duplicate 2
, so we will stop looking for previous smaller if it's the same number, but we won't stop for next smaller.
...2 5 6 2 3 2 4 5 2 8 2 9 1
|---*-------------|
Nice Explanation Video: https://www.youtube.com/watch?v=TZyBPy7iOAw
private val mod = 1000000000 + 7
fun sumSubarrayMins(arr: IntArray): Int {
val n = arr.size
// Index, we have to provide the default value to indicate not found
val nextSmaller = IntArray(n) { n }
val prevSmaller = IntArray(n) { -1 }
// Index, we have to calculate the length of subarray
val stack = Stack<Int>()
for (i in 0 until n) {
while (stack.isNotEmpty() && arr[stack.peek()] > arr[i]) {
val index = stack.pop()
nextSmaller[index] = i
}
stack.push(i)
}
stack.clear()
for (i in n -1 downTo 0) {
// There is "equal" operator, we treat the first duplicate numbers as smaller number
while (stack.isNotEmpty() && arr[i] <= arr[stack.peek()]) {
val index = stack.pop()
prevSmaller[index] = i
}
stack.push(i)
}
// Use Long type to prevent overflow
var result = 0L
for (i in 0 until n) {
val sum: Long = arr[i].toLong() * (nextSmaller[i] - i).toLong() % mod * (i - prevSmaller[i]) % mod
result = (result + sum) % mod
}
return result.toInt()
}
- Time Complexity::
O(n)
to iterate the whole array and each element is pushed and popped at most once. - Space Complexity::
O(n)
for stack and arrays to store the previous and next smaller index.