Skip to content
Go back

Leetcode 1524 - Number of Sub-arrays With Odd Sum

Table of contents

Open Table of contents

題目介紹

題目:計算奇數和子陣列的數量

描述: 給定一個整數陣列 arr,要求計算出所有可能的子陣列(連續的元素序列)中,總和為奇數的子陣列個數。

由於結果可能非常大,需要將答案對 10^9 + 7 取模。

例如:

解題思路

  1. 前綴和的概念

    • 前綴和是指從數組開頭到當前位置的所有數字之和
    • 例如數組 [1,3,5],其前綴和為:
      • index -1: 0
      • index 0: 1
      • index 1: 1+3 = 4
      • index 2: 1+3+5 = 9
  2. 為什麼使用前綴和?

    • 任何子陣列的和都可以用兩個前綴和相減得到
    • 例如要求 index 1 到 2 的子陣列和:
      • 可以用 前綴和[2] - 前綴和[0] = 9 - 1 = 8
    • 這樣可以快速計算任何子陣列的和
  3. 奇偶性質的運用

    • 當我們要找奇數和的子陣列時,可以利用奇偶性質:
      • 偶數 - 偶數 = 偶數
      • 奇數 - 奇數 = 偶數
      • 奇數 - 偶數 = 奇數
      • 偶數 - 奇數 = 奇數
  4. 解題策略

    • 我們要找的是”和為奇數”的子陣列,可以用以下方式來思考:

    • 第一步:追蹤前綴和的奇偶性

      • 從左到右遍歷數組時,持續計算前綴和
      • 只需記錄前綴和的奇偶性(因為我們只關心最後的和是奇數還是偶數)
      • 使用兩個計數器:
        • odd:記錄奇數前綴和出現的次數
        • even:記錄偶數前綴和出現的次數(初始為1,因為空前綴和為0是偶數)
    • 第二步:如何找到奇數和的子陣列?

      • 假設我們現在在位置 i,前綴和為 sum_i
      • 如果要使子陣列和為奇數,我們需要找到一個之前的位置 j,使得:
        • sum_i - sum_j = 奇數
      • 這代表:
        • 如果 sum_i 是奇數,我們需要減去一個偶數前綴和
        • 如果 sum_i 是偶數,我們需要減去一個奇數前綴和
    • 舉例說明:

      • 數組是 [1,3,5]
      • 前綴和序列是:0,1,4,9
      • 在計算過程中:
        • 位置0(和為1):奇數,可以和初始的0(偶數)相減,得到子陣列[1]和為1(奇數)
        • 位置1(和為4):偶數,可以和位置0的1(奇數)相減,得到子陣列[3]和為3(奇數)
        • 位置2(和為9):奇數,可以和:
          • 初始的0(偶數)相減,得到子陣列[1,3,5]和為9(奇數)
          • 位置1的4(偶數)相減,得到子陣列[5]和為5(奇數)
    • 最終統計:

      • 在這個例子中:
        • odd(奇數前綴和)= 2(分別是1和9)
        • even(偶數前綴和)= 2(分別是初始的0和4)
      • 所以總共有 odd × even = 2 × 2 = 4 個奇數和子陣列
  5. 複雜度分析

    • 時間複雜度:O(n)

      • 我們只需要遍歷數組一次,對每個元素進行簡單的計算
      • 每次操作(更新前綴和奇偶性、增加計數器)都是 O(1) 常數時間
      • 因此總時間複雜度就是 O(n),其中 n 是數組長度
    • 空間複雜度:O(1)

      • 我們只使用了有限的變數(even、odd、s)來記錄狀態
      • 這些變數的數量不會隨著輸入數據的大小增加
      • 無論輸入數組有多大,所需的額外空間都是固定的
      • 因此空間複雜度為 O(1) 常數空間
    • 效率對比

      • 暴力解法需要計算所有可能的子數組和,時間複雜度為 O(n²)
      • 我們的解法利用數學性質,一次遍歷就能解決,顯著提升了效率
      • 在處理大規模數據時,這種 O(n) 的解法比暴力方法快得多

代碼實現

class Solution:
    def numOfSubarrays(self, arr: List[int]) -> int:
        mod = 10**9 + 7
        even, odd = 1, 0  # even count initialized for prefix sum parity 0
        s = 0
        for num in arr:
            s ^= (num & 1)  # update parity using XOR
            if s:
                odd += 1
            else:
                even += 1
        return (odd * even) % mod

常見問題與解答

總結

今天我們看的這題「計算奇數和子陣列的數量」真的很有趣!乍看之下,你可能會想著:「嗯,我就把所有子陣列都算出來,然後檢查哪些和是奇數就好了」—但這樣的暴力解法會讓你的程式跑很久(時間複雜度 O(n²))。

而我們學到的這個解法可是聰明多了!用前綴和加上奇偶性的特性,我們只要:

  1. 走一遍陣列,記錄前綴和的奇偶性就好
  2. 不用記住每個前綴和是多少,只要知道「有幾個是奇數、有幾個是偶數」
  3. 用簡單的乘法公式 odd × even 就能算出所有奇數和子陣列的數量

這種解法不僅快(只要 O(n) 時間),而且超級省空間(只用了幾個變數)。

這個技巧真的很實用,不只適用於這題,還可以應用在很多類似的「子陣列和」問題上。下次如果你遇到類似的題目,可以試著想想:我是不是可以用前綴和?是不是只需要關注某些特定的性質,而不是具體的數值呢?

記住這個思路,會讓你在解決子陣列相關的問題時更加得心應手喔!


Share this post on:

Previous Post
Leetcode 1780 - Check if Number is a Sum of Powers of Three
Next Post
🚀 比 pip 還快 100 倍?Python 套件管理新神器:uv