一、题目
给定一个整数数组 arr,找到 min(b) 的总和,其中 b 的范围为 arr 的每个(连续)子数组。
由于答案可能很大,因此 返回答案模 10^9 + 7 。
来源:力扣
二、思路
1.读完题之后先尝试了暴力解题,即依次遍历出所有连续子数组的最小值然后求和,提交之后在第68样例超时。
2.优化时间复杂度之后的思路:对数组中的每一个数找到他对应的“辐射区域”(与之相邻的连续子数组最小值都是这个数),求和时只需算每一个数的辐射区域的子数组个数✖该数即可。
每个中心点包含的子序列个数计算公式:(index(中心点) - index(辐射区第1个整数) + 1) * (index(辐射区最后1个整数) - index(中心点) + 1)
确定辐射区域:采用单调栈的方式,因为计算辐射区内的子序列时,是需要通过下标计算的,所以堆栈中存储的是数组arr中元素的下标。然后,遍历整个数组arr,如果堆栈为空,那么直接入栈;如果堆栈不为空,则进行如下操作:
如果栈顶元素 > arr[i] :则弹出栈顶元素n,那么针对于这个n来说,它的辐射区域就是(新的栈顶元素, i)[注]左开右开区间。
如果栈顶元素 <= arr[i] :则arr[i]直接入栈。
三、代码
方法一:
class Solution:
def sumSubarrayMins(self, arr: List[int]) -> int:
L=len(arr)
sum1=0
K=1
b=[]
for i in range(L):
a=[]
for k in range(L-i):
while k>=0:
a.append(arr[i+k])
k -=1
b.append(min(a))
for i in range(len(b)):
sum1=sum1+b[i]
return sum1
方法二:
class Solution:
def sumSubarrayMins(self, arr: List[int]) -> int:
n = len(arr)
left = [-1] * n
right = [n] * n
stk = []
for i, v in enumerate(arr):
while stk and arr[stk[-1]] >= v:
stk.pop()
if stk:
left[i] = stk[-1]
stk.append(i)
stk = []
for i in range(n - 1, -1, -1):
while stk and arr[stk[-1]] > arr[i]:
stk.pop()
if stk:
right[i] = stk[-1]
stk.append(i)
mod = 10**9 + 7
return sum((i - left[i]) * (right[i] - i) * v for i, v in enumerate(arr)) % mod