907. Sum of Subarray Minimums

思路
实际上是要求
res = sum(A[i] * f(i)),其中f(i)是子数组的数量,A[i]是最小值。

为了得到f(i),我们要得到

  • left[i],能在A[i]左侧取得的最长宽度,使得在这个范围内A[i]是最小值,且都大于A[i]
  • right[i], 能在A[i]右侧取得的最长宽度,使得在这个范围内A[i]是最小值,且都大于等于A[i]

然后,

  • A[i]左侧有left[i] + 1种连续子数组的取法
  • A[i]右侧有right[i] + 1种连续子数组的取法

最后根据f(i) = (left[i] + 1) * (right[i] + 1)即可求得f(i)
所以这个问题在于求left[i]right[i]

举例,对于[3,1,2,4]
left + 1 = [1,2,1,1]
right + 1 = [1,3,2,1]
f = [1,6,2,1]
res = 3 * 1 + 1 * 6 + 2 * 2 + 4 * 1 = 17

那么,解释一下为什么left[i]要取大于A[i],而right[i]只需要取大于等于A[i]
他是为了解决数组中有相等的数的情况。
假设有数组...a1...a2...a3... ...an...其中,a1=a2=a3=...=an,那么这个连续数组的各种组合中,起点可以起自...a1, ...a2, ...a3,直到...an,而终点则可以尽可能地往右边取。

所以在i=a1处,left[i]最多取到a1,而在i=a2处,left[i]最少也取到a1+1。如此类推,起点可以从...a1...a2一直取到an,而终点可以从起点一直取到最右侧(只要大于等于起点的值),如此可以把全部的连续子数组的情况覆盖。

算法

那么怎么求得left[i]right[i]呢?我们可以看以下两个同类型的问题的讨论:

  • LC901. Online Stock Span
  • LC84. Largest Rectangle in Histogram

答案主要有两种做法,一种是用栈,一种是用数组。

解决方案1:栈
[C++/Java/Python] O(1)
就是用increasing stack来解决问题。

解决方案2:数组
5ms O(n) Java solution explained (beats 96%)
本来遍历要一个一个遍历的,像这样:

for (int i = 1; i < height.length; i++) {              
    int p = i - 1;
    while (p >= 0 && height[p] >= height[i]) {
        p--;
    }
    lessFromLeft[i] = p;              
}

但我们可以改良它,在往前遍历时不一个一个地遍历,而是利用之前的计算结果,直接“跳”过多个元素:

while (p >= 0 && height[p] >= height[i]) {
      p = lessFromLeft[p];
}

你可能感兴趣的:(907. Sum of Subarray Minimums)