Leetcode 1248. 統計「優美子數組」【記錄奇數位置&滑動窗口&前綴和】


本文在別人的題解【鏈接列於參考資料中】基礎上加上些許自己的理解,若侵權立刪。

問題描述

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

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

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

示例 1:

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

解題報告

記錄奇數位置

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

所以 問題的關鍵就變成如何高效的求每個奇數前 連續的偶數 的個數

一種解決方案是記錄每個奇數所在的位置,那麼每個奇數前 連續的偶數 個數爲當前奇數的位置減去上一個奇數的位置

另外在實現的時候需要注意一些細節問題,例如第一個奇數前 連續偶數的個數 如何求?最後一個奇數前 連續偶數的個數 如何求?

解決方案是:在最開始添加上虛擬位置 -1,在最後添加虛擬位置 n

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

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

滑動窗口

仔細想一想,上面的思想完全可以用滑動窗口來實現,省去記錄每個 1 的空間。

  • 移動窗口的右邊界時,更新窗口內 1 出現的次數;
  • 當窗口內 1 的數目達到要求的 k 時,開始縮收左邊界;
  • 移動左邊界時,記錄縮收過程遇到的 0 的個數 even,直到遇到 1
  • 累加當前的 even

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

前綴和

這種 做法哈希表優化系列【空間換時間】-Leetcode 560.和爲 K 的子數組 有些許相似之處,都用了前綴和。不同的其中一點是:在 Leetcode 560 中,由於前綴和存在負數的可能,所以需要用哈希表來存儲前綴和,便於查找,但是此題中,前綴和的範圍是可控的而且爲正整數,所以我們完全可以將前綴和作爲 vector 的索引來存儲。

同樣都是前綴和,但是具體實現上,我這種菜雞,還是挺困難的。具體想法和滑動窗口有些相似。

實現代碼

記錄奇數位置實現

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;
    }
};

滑動窗口實現

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;
    }
};

前綴和+哈希表優化實現

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. 統計「優美子數組」
[2] 【每日算法Day 107】面試必考:良心推薦,一題三解,不看後悔一輩子

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