每日一題,每日一練28.統計[優美子數組](有人相愛,有人半夜看海,有人早上八點寫不出來)

統計「優美子數組」 給你一個整數數組 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

寫了兩個小時的滑動窗口,改了無數次條件,終於意識到了這不是僅僅要改窗口大小的事,被迫滾去看題解。然後又花了一個小時弄懂題解(我覺得各個學科都是不用專業名詞很好懂)。簡單解釋一下。
由於數組中只有奇數有用,我們可以設立一個數組,用來記錄第i個奇數前面帶了幾個偶數(不是奇數就是偶數)
比如[212221],第一個奇數前面帶了一個偶數,第二個帶了三個,所以數組是[1,3]

如果奇數不到k個,顯然沒有子數組,那麼從第k個奇數開始,將每個奇數作爲子數組的終點,因爲子數組要有k個奇數,所以子數組第一個數如果是奇數必須是第一個奇數,這是極限狀態,然後,我們發現這個子數組還可以在前面塞偶數,就是我們第一個奇數在數組的記錄值。於是我們這樣遞推下去,對第i個奇數,可以組成的子數組都是極限狀態+第i-k+1個奇數攜帶的偶數(可以塞在數組最前面

那麼什麼時候可以塞在最後面呢,這裏我們還是基於極限狀態來考慮。很顯然,只有偶數能塞在極限狀態的後面,如果我們塞進去一個奇數,就要修改極限狀態

那麼,當極限狀態已經形成(統計已經到k個奇數時),如果遍歷再獲得奇數,我們當然記錄到奇數數組並確立新的極限狀態和狀況**,如果是偶數,我們就可以插在之前極限狀態的後面,這時候要注意,新增的符合條件的數組並不是加1,由於極限狀態的前插和後插不互相干涉,我們可以認爲之前的每種前插狀況都可以和後插組成符合條件**

於是,我們的思路流程如下

記錄每個奇數前面攜帶的偶數

從第i(i>=k)個奇數開始,把這個奇數作爲子數組終點

由於子數組要有k個奇數,找到符合條件的第一個奇數(i-k)

確立子數組的極限狀態,也就是兩邊都爲奇數且奇數爲k,不能再減少了

第一個奇數在前面能插入多少偶數,就有多少種滿足條件的子數組

在存在極限狀態後,分支如下:
如果接下來記錄奇數,執行原思路流程

**如果接下來是偶數,疊加一次最近的極限狀態滿足條件的子數組,比如最近的奇數所確立的極限狀態裏第一個奇數前面攜帶4個偶數,則有極限狀態+4種前插總計5個子數組,那麼每後插一個偶數則咋滿足條件的數目ans=ans+5

代碼如下(官方的代碼,因爲再手撕好像變不出來什麼新的花樣了==0)

class Solution:
    def numberOfSubarrays(self, nums: List[int], k: int) -> int:
        cnt = [0] * (len(nums) + 1)
        cnt[0] =1
        odd, ans = 0, 0
        for num in nums:
            if num % 2 == 1:
                odd += 1
            if odd >= k:
                ans += cnt[odd - k]#
            cnt[odd] += 1#
        return ans
#作者:LeetCode-Solution
#鏈接:https://leetcode-cn.com/problems/count-number-of-nice-#subarrays/solution/tong-ji-you-mei-zi-shu-zu-by-leetcode-solution/
#來源:力扣(LeetCode)
#著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

註釋太黑了我們按行解釋一下

cnt = [0] * (len(nums) + 1) ### 創建奇數攜帶記錄數組

cnt[0] =1 ##這裏是給第一個奇數本身先佔一個位置,也就是我們的極限狀態

odd, ans = 0, 0 ## odd代表當前記錄第幾個奇數,ans代表答案

for num in nums: ##開始遍歷原數組

if num % 2 == 1:#如果是奇數,意味着這個奇數已經被記錄完成,因爲這個奇數本身我們佔過位了

odd += 1 #我們開始專注應對下一個奇數

if odd >= k:#如果已經有極限狀態存在

ans += cnt[odd - k]#如果是奇數,我們將新的奇數當成子數組最後一個奇數,確立新的極限狀態和可以前插的數目(odd位移過了),如果是偶數,我們疊加最新的極限狀態可以前插的數目(理由如上)(odd沒有位移

cnt[odd] += 1# 如果是奇數,這裏爲這個奇數佔位,如果是偶數,增加這個奇數的攜帶偶數一個

return ans#循環整個數組後返回答案

現在再回去看看代碼,是否清楚一點呢

有人相愛,有人半夜看海,有人早上八點寫不出來

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