【每日算法Day 107】面試必考:良心推薦,一題三解,不看後悔一輩子

可能有些同學只會寫 python ,看不懂 c++。但是一個是因爲我懶,多解時不想再寫一遍 python 了,一個是理解算法最重要,語言不重要。今天學妹發來一張圖,我覺得說的很好。

v2-65c2361774b73a17166aa67839ff533d_b.jpg
院長大大如是說

題目鏈接

LeetCode 1248. 統計「優美子數組」[1]

題目描述

給你一個整數數組 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 ,偶數變爲 0 ,不難發現這是等價於原來題意的。

統計奇數位置

我們發現,如果兩個 1 之間(包含自身)一共包含了 k1 ,那麼這 k1 可以構成的連續子數組個數就是 左邊 0 的個數加一 乘上 右邊 0 的個數加一

那麼如何統計每個 1 前後 0 的個數呢?其實只需要記錄一下每個 1 的位置,然後直接用相鄰兩個 1 的位置相減就可以得到中間 0 的個數加一了。

所以直接記錄每個 1 的位置,爲了處理邊界,我們還需要在最開始添加上虛擬位置 -1 ,在最後添加虛擬位置 n

然後對於第 i1 來說,如果將它作爲子數組第一個 1 ,那麼最後一個 1 應該是第 i+k-11 。所以直接計算兩邊 0 的數量,再加一相乘就行了:

(pos[i] - pos[i-1])\cdot(pos[i+k] - pos[i+k-1]) \\

最後遍歷所有的 i ,將第 i1 作爲起點,然後累加答案就行了。

時間複雜度 O(n) ,空間複雜度 O(n)

雙指針

主要思想還是跟上面方法一樣,但是不用直接計算 左邊 0 的個數加一 乘上 右邊 0 的個數加一。只需要計算 左邊 0 的個數加一 ,然後右指針如果指着 0 ,就加上這個值,一直加到右指針爲 1 爲止。

所以只需要用常數個變量就行了,even 記錄子數組前面有多少個 0cnt 記錄當前子數組有多少個 1 。用 l 指向子數組開頭,r 指向子數組結尾。

如果 cnt = k ,那就說明子數組中正好有 k1 。那就右移 l ,直到遇到 1 爲止,這樣就能統計出左邊有多少個 0 ,記錄在 even 中。然後 l 右移跳過這個 1 ,同時 cnt 減一。

如果 cnt < k ,那就說明 1 的數量不夠,r 繼續右移就行了。同時每移動一次,答案都要加上 even 值,因爲你之前 cnt = k 時記錄了一下左邊 0 的數量,現在右邊每一個 0 都得加上它。其實除了初始階段,其餘時候 cnt 都是等於 k-1 的。而初始階段 even = 0 ,所以加上也沒事,可以合併寫。

時間複雜度 O(n) ,空間複雜度 O(1)

前綴和

遍歷原數組中每個位置 i,如果 i 之前(包含自身) 1 的個數一共 odd 個(也就是前綴和),那麼我們只需要看有多少個位置 j < i 滿足 1 的前綴和等於 odd-k ,那麼 [j+1, i] 就是正好包含 k1 的子數組。

所以我們只需要用一個計數數組來記錄一下前綴和對應的出現次數就行了,然後每次取出 odd-k 的次數加到答案裏就行了。

時間複雜度 O(n) ,空間複雜度 O(n)

代碼

統計奇數位置(c++)

        class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> pos;
        pos.push_back(-1);
        for (int i = 0; i < n; ++i) {
            if (nums[i]&1) pos.push_back(i);
        }
        pos.push_back(n);
        int res = 0, sz = pos.size();
        for (int i = 1; i+k < sz; ++i) {
            res += (pos[i] - pos[i-1]) * (pos[i+k] - pos[i+k-1]);
        }
        return res;
    }
};

      

雙指針(c++)

        class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        int res = 0, cnt = 0, even = 0;
        int l = 0, r = 0;
        while (r < n) {
            if (cnt < k && (nums[r++]&1)) cnt++;
            if (cnt == k) {
                even = 1;
                while (!(nums[l++]&1)) even++;
                cnt--;
            }
            res += even;
        }
        return res;
    }
};

      

前綴和(c++)

        class Solution {
public:
    int numberOfSubarrays(vector<int>& nums, int k) {
        int n = nums.size();
        vector<int> count(n+1, 0);
        count[0] = 1;
        int res = 0, odd = 0;
        for (int i = 0; i < n; ++i) {
            odd += nums[i]&1;
            if (odd >= k) res += count[odd-k];
            count[odd]++;
        }
        return res;
    }
};

      

參考資料

[1]

LeetCode 1248. 統計「優美子數組」: leetcode-cn.com/problem

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