-
Notifications
You must be signed in to change notification settings - Fork 119
/
Copy pathSum of Subarray Ranges.py
42 lines (37 loc) · 2.32 KB
/
Sum of Subarray Ranges.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Solution:
def subArrayRanges(self, nums: List[int]) -> int:
n = len(nums)
# the answer will be sum{ Max(subarray) - Min(subarray) } over all possible subarray
# which decomposes to sum{Max(subarray)} - sum{Min(subarray)} over all possible subarray
# so totalsum = maxsum - minsum
# we calculate minsum and maxsum in two different loops
minsum = maxsum = 0
# first calculate sum{ Min(subarray) } over all subarrays
# sum{ Min(subarray) } = sum(f(i) * nums[i]) ; i=0..n-1
# where f(i) is number of subarrays where nums[i] is the minimum value
# f(i) = (i - index of the previous smaller value) * (index of the next smaller value - i) * nums[i]
# we can claculate these indices in linear time using a monotonically increasing stack.
stack = []
for next_smaller in range(n + 1):
# we pop from the stack in order to satisfy the monotonically increasing order property
# if we reach the end of the iteration and there are elements present in the stack, we pop all of them
while stack and (next_smaller == n or nums[stack[-1]] > nums[next_smaller]):
i = stack.pop()
prev_smaller = stack[-1] if stack else -1
minsum += nums[i] * (next_smaller - i) * (i - prev_smaller)
stack.append(next_smaller)
# then calculate sum{ Max(subarray) } over all subarrays
# sum{ Max(subarray) } = sum(f'(i) * nums[i]) ; i=0..n-1
# where f'(i) is number of subarrays where nums[i] is the maximum value
# f'(i) = (i - index of the previous larger value) - (index of the next larger value - i) * nums[i]
# this time we use a monotonically decreasing stack.
stack = []
for next_larger in range(n + 1):
# we pop from the stack in order to satisfy the monotonically decreasing order property
# if we reach the end of the iteration and there are elements present in the stack, we pop all of them
while stack and (next_larger == n or nums[stack[-1]] < nums[next_larger]):
i = stack.pop()
prev_larger = stack[-1] if stack else -1
maxsum += nums[i] * (next_larger - i) * (i - prev_larger)
stack.append(next_larger)
return maxsum - minsum