力扣打卡第六天 子数组的最小值之和

子数组的最小值之和

方法一:单调栈
考虑所有满足以数组 arr\textit{arr}arr 中的某个元素 arr[i]\textit{arr}[i]arr[i] 为最右且最小的元素的子序列个数 C[i]C[i]C[i],那么题目要求求连续子数组的最小值之和即为 ∑i=0n−1arr[i]×C[i]\sum_{i=0}^{n-1} \limits \textit{arr}[i] \times C[i]
i=0

n−1

arr[i]×C[i],其中数组 arr\textit{arr}arr 的长度为 nnn。我们必须假设当前元素为最右边且最小的元素,这样才可以构造互不相交的子序列,否则会出现多次计算,因为一个数组的最小值可能不唯一。

经过以上思考,我们只需要找到每个元素 arr[i]\textit{arr}[i]arr[i] 以该元素为最右且最小的子序列的数目 left[i]\textit{left}[i]left[i],以及以该元素为最左且最小的子序列的数目 right[i]\textit{right}[i]right[i],则以 arr[i]\textit{arr}[i]arr[i] 为最小元素的子序列的数目合计为 left[i]×right[i]\textit{left}[i] \times \textit{right[i]}left[i]×right[i]。对于数组中每个元素 arr[i]\textit{arr}[i]arr[i],具体做法如下:

求左边第一个小于 arr[i]\textit{arr}[i]arr[i] 的元素:从左向右遍历数组,并维护一个单调递增的栈,遍历当前元素 arr[i]\textit{arr}[i]arr[i],如果遇到当前栈顶的元素大于等于 arr[i]\textit{arr}[i]arr[i] 则将其弹出,直到栈顶的元素小于 arr[i]\textit{arr}[i]arr[i],栈顶的元素即为左边第一个小于 arr[i]\textit{arr}[i]arr[i] 的元素 arr[j]\textit{arr}[j]arr[j],此时 left[i]=i−j\textit{left}[i] = i - jleft[i]=i−j。

求右边第一个大于等于 arr[i]\textit{arr}[i]arr[i] 的元素:从右向左遍历数组,维护一个单调递增的栈,遍历当前元素 arr[i]\textit{arr}[i]arr[i],如果遇到当前栈顶的元素大于 arr[i]\textit{arr}[i]arr[i] 则将其弹出,直到栈顶的元素小于等于 arr[i]\textit{arr}[i]arr[i],栈顶的元素即为右边第一个小于等于 arr[i]\textit{arr}[i]arr[i] 的元素 arr[k]\textit{arr}[k]arr[k],此时 right[i]=k−i\textit{right}[i] = k - iright[i]=k−i。

连续子数组 arr[j],arr[j+1],⋯ ,arr[k]\textit{arr}[j], \textit{arr}[j + 1], \cdots, \textit{arr}[k]arr[j],arr[j+1],⋯,arr[k] 的最小元素即为 arr[i]\textit{arr}[i]arr[i],以 arr[i]\textit{arr}[i]arr[i] 为最小元素的连续子序列的数量为 (i−j)×(k−i)(i - j) \times (k - i)(i−j)×(k−i)。

arr[i]×left[i]×right[i]。维护单调栈的过程线性的,因为只进行了线性次的入栈和出栈。

MOD = 10 ** 9 + 7

class Solution:
    def sumSubarrayMins(self, arr: List[int]) -> int:
        n = len(arr)
        monoStack = []
        left = [0] * n
        right = [0] * n
        for i, x in enumerate(arr):
            while monoStack and x <= arr[monoStack[-1]]:
                monoStack.pop()
            left[i] = i - (monoStack[-1] if monoStack else -1)
            monoStack.append(i)
        monoStack = []
        for i in range(n - 1, -1, -1):
            while monoStack and arr[i] < arr[monoStack[-1]]:
                monoStack.pop()
            right[i] = (monoStack[-1] if monoStack else n) - i
            monoStack.append(i)
        ans = 0
        for l, r, x in zip(left, right, arr):
            ans = (ans + l * r * x) % MOD
        return ans

猜你喜欢

转载自blog.csdn.net/qq_46157589/article/details/127584715
今日推荐