1. 程式人生 > >[LeetCode] 907. Sum of Subarray Minimums

[LeetCode] 907. Sum of Subarray Minimums

題目

Given an array of integers A, find the sum of min(B), where B ranges over every (contiguous) subarray of A.

Since the answer may be large, return the answer modulo 10^9 + 7.

Example 1:

Input: [3,1,2,4]
Output: 17
Explanation: Subarrays are [3], [1], [2], [4], [3,1], [1,2], [2,4], [3,1,2], [1,2,4], [3,1,2,4]. 
Minimums are 3, 1, 2, 4, 1, 1, 2, 1, 1, 1.  Sum is 17.

Note:

  1. 1 <= A.length <= 30000
  2. 1 <= A[i] <= 300001 <= A[i] <= 30000

題目大意

生成 陣列的所有連續 子串,將所有子串的最小值求和。

思路

方法一 time O(n2) 超時

動態規劃 狀態 minArr[i][j] :子串長度為 (i+1)時,以 index = j 開頭的子串的 最小值。 狀態初始化:minArr[0][j] = A[j] ,子串長度為1時,子串最小值是它本身。 狀態轉移方程:minArr[i][j] = Math.min(minArr[i-1][j],A[j+i]); 以j開頭,長度為 i+1的子串的最小值為 MIN(長度為 i子串的最小值,與 A[i+j]) 。 由於 只用到 minArr[i][j] 只用到 前一個 minArr[i-1][j],故可以 簡化成一維。

結果為: int res =0; res += minArr[i][j];

由於 A.length 太大,超時。

class Solution {
    public int sumSubarrayMins(int[] A) {
        int[] minArr = A.clone();
        int res = 0;
        for(int i = 0 ; i < A.length;i++)
            for(int j  = 0;j+i<A.length;j++){
                minArr[j] = Math.min(minArr[
j],A[i+j]); res += minArr[j]; res %= 1000000007; } return res; } }

方法二 計算 每個數 在結果中出現的次數

對於A[i] 設 j <= i ,且 A[j],A[j-1] ……,A[i-1] >= A[i] 設 k >= i ,且 A[i+1]……,A[k-1] ,A[k] > A[i] (計算 j 中 使用 >= ,k中 使用 > 。是因為 A中可能有重複的數,計運算元串時,我們取 子串 中最後出現的最小數 為 子串的唯一最小數)

故 A[i] 在結果計算中出現的次數為 (k-j+1+(k-i)(i-j)) k-j+1 以A[i]為邊界 的 子串。 (k-i)(i-j) 以 A[i]左邊 為左邊界 ,以 A[i]右邊 為右邊界 的 子串。

class Solution {
    public int sumSubarrayMins(int[] A) {
        int res = 0;
        for(int i = 0 ; i < A.length;i++){
            int j = i,k = i;
            while(j-1>=0 && A[j-1]>=A[i])
                j--;
            while(k+1<A.length && A[k+1]>A[i])
                k++;
            res =(res+(k-j+1+(k-i)*(i-j))*A[i])%1000000007;
        }
        return res;
    }
}

Your runtime beats 2.64 % of java submissions.

用時很長,可以在計算邊界時繼續優化。

class Solution {
    public int sumSubarrayMins(int[] A) {
        int res = 0;
        int[] rdp = new int[A.length];
        int[] ldp = new int[A.length];

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

        for(int i = 0;i<A.length;i++){
            ldp[i] = i-1;
            while(ldp[i]>=0 && A[ldp[i]] >= A[i])
                ldp[i] = ldp[ldp[i]];
        }
        
        for(int i = 0 ; i < A.length;i++){
            int j = ldp[i]+1,k = rdp[i]-1;
            res =(res+(k-j+1+(k-i)*(i-j))*A[i])%1000000007;
        }
        return res;
    }
}

Your runtime beats 95.92 % of java submissions.