[LeetCode] Number of Subarrays with Bounded Maximum 有界限最大值的子數組數量


 

We are given an array A of positive integers, and two positive integers L and R (L <= R).

Return the number of (contiguous, non-empty) subarrays such that the value of the maximum array element in that subarray is at least L and at most R.

Example :
Input: 
A = [2, 1, 4, 3]
L = 2
R = 3
Output: 3
Explanation: There are three subarrays that meet the requirements: [2], [2, 1], [3].

Note:

  • L, R  and A[i] will be an integer in the range [0, 10^9].
  • The length of A will be in the range of [1, 50000].

 

這道題給了我們一個數組,又給了我們兩個數字L和R,表示一個區間范圍,讓我們求有多少個這樣的子數組,使得其最大值在[L, R]區間的范圍內。既然是求子數組的問題,那么最直接,最暴力的方法就是遍歷所有的子數組,然后維護一個當前的最大值,只要這個最大值在[L, R]區間的范圍內,結果res自增1即可。但是這種最原始,最粗獷的暴力搜索法,OJ不答應。但是其實我們略作優化,就可以通過了。優化的方法是,首先,如果當前數字大於R了,那么其實后面就不用再遍歷了,不管當前這個數字是不是最大值,它都已經大於R了,那么最大值可能會更大,所以沒有必要再繼續遍歷下去了。同樣的剪枝也要加在內層循環中加,當curMax大於R時,直接break掉內層循環即可,參見代碼如下:

 

解法一:

class Solution {
public:
    int numSubarrayBoundedMax(vector<int>& A, int L, int R) {
        int res = 0, n = A.size();
        for (int i = 0; i < n; ++i) {
            if (A[i] > R) continue;
            int curMax = INT_MIN;
            for (int j = i; j < n; ++j) {
                curMax = max(curMax, A[j]);
                if (curMax > R) break;
                if (curMax >= L) ++res;
            }
        }
        return res;
    }
};

 

雖然上面的方法做了剪枝后能通過OJ,但是我們能不能在線性的時間復雜度內完成呢。答案是肯定的,我們先來看一種官方解答貼中的方法,這種方法是用一個子函數來算出最大值在[-∞, x]范圍內的子數組的個數,而這種區間只需要一個循環就夠了,為啥呢?我們來看數組[2, 1, 4, 3]的最大值在[-∞, 4]范圍內的子數組的個數。當遍歷到2時,只有一個子數組[2],遍歷到1時,有三個子數組,[2], [1], [2,1]。當遍歷到4時,有六個子數組,[2], [1], [4], [2,1], [1,4], [2,1,4]。當遍歷到3時,有十個子數組。其實如果長度為n的數組的最大值在范圍[-∞, x]內的話,其所有子數組都是符合題意的,而長度為n的數組共有n(n+1)/2個子數組,剛好是等差數列的求和公式。所以我們在遍歷數組的時候,如果當前數組小於等於x,則cur自增1,然后將cur加到結果res中;如果大於x,則cur重置為0。這樣我們可以正確求出最大值在[-∞, x]范圍內的子數組的個數。而要求最大值在[L, R]范圍內的子數組的個數,只需要用最大值在[-∞, R]范圍內的子數組的個數,減去最大值在[-∞, L-1]范圍內的子數組的個數即可,參見代碼如下:

 

解法二:

class Solution {
public:
    int numSubarrayBoundedMax(vector<int>& A, int L, int R) {
        return count(A, R) - count(A, L - 1);
    }
    int count(vector<int>& A, int bound) {
        int res = 0, cur = 0;
        for (int x : A) {
            cur = (x <= bound) ? cur + 1 : 0;
            res += cur;
        }
        return res;
    }
};

 

下面這種解法也是線性時間復雜度的,跟上面解法的原理很類似,只不過沒有寫子函數而已。我們使用left和right來分別標記子數組的左右邊界,使得其最大值在范圍[L,R]內。那么當遍歷到的數字大於等於L時,right賦值為當前位置i,那么每次res加上right - left,隨着right的不停自增1,每次加上的right - left,實際上也是一個等差數列,跟上面解法中的子函數本質時一樣的。當A[i]大於R的時候,left = i,那么此時A[i]肯定也大於等於L,於是rihgt=i,那么right - left為0,相當於上面的cur重置為0的操作,發現本質聯系了吧,參見代碼如下:

 

解法三:

class Solution {
public:
    int numSubarrayBoundedMax(vector<int>& A, int L, int R) {
        int res = 0, left = -1, right = -1;
        for (int i = 0; i < A.size(); ++i) {
            if (A[i] > R) left = i;
            if (A[i] >= L) right = i;
            res += right - left;
        }
        return res;
    }
};

 

我們可以對上面的解法稍稍做下優化,在A[i] > R的時候,left和right都賦值為i,然后continue,這樣省去了后面的用0來更新結果res的步驟,能提高一些運行效率,參見代碼如下:

 

解法四:

class Solution {
public:
    int numSubarrayBoundedMax(vector<int>& A, int L, int R) {
        int res = 0, left = -1, right = -1;
        for (int i = 0; i < A.size(); ++i) {
            if (A[i] > R) {
                left = right = i;
                continue;
            }
            if (A[i] >= L) right = i;
            res += right - left;
        }
        return res;
    }
};

 

參考資料:

https://leetcode.com/problems/number-of-subarrays-with-bounded-maximum/solution/

https://leetcode.com/problems/number-of-subarrays-with-bounded-maximum/discuss/117585/Java-9-liner

https://leetcode.com/problems/number-of-subarrays-with-bounded-maximum/discuss/117616/C++-O(n)-less10-lines

https://leetcode.com/problems/number-of-subarrays-with-bounded-maximum/discuss/119162/Clean-and-simple-O(n)-Java

 

LeetCode All in One 題目講解匯總(持續更新中...)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM