統計「優美子數組」-- 滑動窗口

0x01.問題

給你一個整數數組 nums 和一個整數 k

如果某個 連續 子數組中恰好有 k 個奇數數字,我們就認爲這個子數組是 「優美子數組」

請返回這個數組中「優美子數組」的數目。

示例 1:

輸入:nums = [1,1,2,1,1], k = 3 輸出:2 解釋:包含 3 個奇數的子數組是 [1,1,2,1] 和
[1,2,1,1] 。

示例 2:

輸入:nums = [2,4,6], k = 1 輸出:0 解釋:數列中不包含任何奇數,所以不存在優美子數組。

示例 3:

輸入:nums = [2,2,2,1,2,2,1,2,2,2], k = 2 輸出:16

提示:

  • 1 <= nums.length <= 50000
  • 1 <= nums[i] <= 10^5
  • 1 <= k <= nums.length
public int numberOfSubarrays(int[] nums, int k) 

0x02.詳細思路分析

第一步,讀題,發現題目的需求:

  • 在數組中找出一些連續子數組,統計這些連續子數組的數目。
  • 這些連續子數組需要滿足的條件是,奇數個數等於k

理清一下思路,發現着重點在於尋找子數組的數目,於是,一條很明顯的思路就出來了,那就是,遍歷所有可能的連續子數組,簡單的寫出下列的代碼:

class Solution {
    public int numberOfSubarrays(int[] nums, int k) {
        int ans=0;
        for(int i=0;i<nums.length;i++){
            int cou=0;
            for(int j=i;j<nums.length;j++){
                if((nums[j]&1)==1){
                    cou++;
                }
                if(cou==k){
                    ans++;
                }
                if(cou>k){
                    break;
                }
            }
        }
        return ans;
    }
}

應該可以說已經解決了這個問題,但我們看一下時間複雜度:O(N^2),這很明顯太高了,當數據量達到10^5的時候,已經無法短時間運算出來了。

我們需要對這個算法進行優化。

其實我們在寫這個算法的時候應該已經感覺到這是一種不好的思路,爲什麼呢?因爲我們在寫這個代碼的時候,就會發現,過於暴力,感覺到似乎做了很多不必要的重複運算。

其實我們的這種感覺,在指引着我們來對算法進行優化,我們的感覺是,重複運算的次數過多。在每一次更換子數組的起始點的時候,我們都要對後面的每個數進行判斷,但其實,這裏面的數很多已經在前面都判斷過了,我們想,如果只判斷一次就好了。

我們判斷這些數的目的是什麼?目的就是統計這個區間內奇數的數量,然後判斷這個子數組是否滿足條件。

我們要想突破O(N^2)的這個瓶頸,就一定不能去遍歷子數組,因爲遍歷子數組的話,O(N^2)是絕對少不了的。那麼我們就只剩下另一條思路,由奇數的個數,去計算出滿足條件的子數組的數目

假如,我們知道了在一段區間內,恰好有k個奇數,我們怎麼去計算有多少滿足條件的連續子數組數目呢?

  • 首先,對於這段區間,最後一個數肯定是奇數,因爲是通過最後一個數判斷出來奇數個數等於k的,這段區間的第一個數暫時不清楚。
  • 然後,對於求連續子數組的個數,最重要的是,確定子數組的起始點和終點,只要在起始點和終點之內包含了k個奇數,那麼都是滿足條件的。
  • 假如,我們從第一個數開始向右邊尋找這段區間內的第一個奇數,並記錄下中間偶數的數目,這些偶數都是可以作爲連續子數組的起始點的,因爲不管怎樣,這段區間的第一個奇數都還在它們後面。
  • 再從最後一個數開始,向右去尋找下一個奇數,並記錄中間偶數的數目,這些偶數都是可以當作連續子數組的尾部的,因爲這段區間內最後一個奇數已經出現。
  • 最後這段區間內的連續子數組的個數就是:起始點數目*終點數目,也就是這些的組合數。

通過上述思路,我們可以基本確定,如何遍歷一次去得出滿足條件的子數組的個數,接下來,確定整體的思路:

  • 採用滑動窗口的思路,每次構造出一段滿足區間內奇數個數恰爲k的區間,然後計算這段區間。
  • 計算完畢後,左指針右移,相應的奇數奇數器減一,不斷重複上述過程。

0x03.解決代碼–滑動窗口

class Solution {
    public int numberOfSubarrays(int[] nums, int k) {
        int ans=0;
        int oddCou=0;
        int left=0;
        int right=0;
        while(right<nums.length){
            if((nums[right++]&1)==1){
                oddCou++;
            }
            if(oddCou==k){
                int preRight=right;
                while(right<nums.length&&(nums[right]&1)==0){
                    right++;
                }
                int rightEvenCou=right-preRight;
                int leftEvenCou=0;
                while((nums[left]&1)==0){
                    leftEvenCou++;
                    left++;
                }
                ans+=(leftEvenCou+1)*(rightEvenCou+1);
                left++;
                oddCou--;
            }
        }
        return ans;
    }
}

ATFWUS --Writing By 2020–04-21

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章