↓快轉到主要內容
  1. LeetCode/

LeetCode 2044: Count Number of Maximum Bitwise-OR Subsets

·2 分鐘· ·
LeetCode Medium Backtracking Bit-Manipulation Enumeration
Wei Yi Chung
作者
Wei Yi Chung
Working at the contributing of open source, distributed systems, and data engineering.
目錄

基本資料
#

難易度: medium 第一次嘗試:2025-07-29

  • 提交紀錄:Accepted → Accepted(位元遮罩版)→ Accepted(回溯版重寫)

解題思路
#

  1. 最大的 OR 一定是全部元素 OR 起來:OR 只會讓位元變多,不會變少,所以選越多不會越小。
  2. 接著數有幾個非空子集合的 OR 等於這個最大值。

n <= 16,子集合最多 2^16 = 65536 個,全部枚舉也可以。

解法一:回溯 + 剪枝
#

每個元素選或不選。一旦目前的 OR 已經等於最大值,剩下的元素不管怎麼選,OR 都不會變,所以剩下 n - i 個元素有 2^(n - i) 種選法,直接回傳。

class Solution:
    def countMaxOrSubsets(self, nums: List[int]) -> int:
        n = len(nums)
        max_or = 0
        for x in nums:
            max_or |= x

        def backtrack(i: int, cur: int) -> int:
            if cur == max_or:
                return 1 << (n - i)               # 剩下的隨便選
            if i == n:
                return 0
            return backtrack(i + 1, cur | nums[i]) + backtrack(i + 1, cur)

        return backtrack(0, 0)

cur 從 0 開始,空集合的 OR 是 0。只要 max_or > 0,空集合就不會被算到;題目保證 nums[i] >= 1,所以 max_or 一定大於 0。

解法二:位元遮罩枚舉
#

class Solution:
    def countMaxOrSubsets(self, nums: List[int]) -> int:
        n = len(nums)
        max_or = 0
        for x in nums:
            max_or |= x
        res = 0
        for mask in range(1, 1 << n):
            cur = 0
            for i in range(n):
                if mask >> i & 1:
                    cur |= nums[i]
            res += cur == max_or
        return res

這是 O(n · 2^n),我交的時候跑了 1320ms,比回溯版慢很多。

複雜度
#

解法時間
回溯 + 剪枝最壞 O(2^n),實際上剪枝很有效
位元遮罩O(n · 2^n)

要特別避免的錯誤
#

  • 以為最大 OR 要另外搜尋:全部 OR 起來就是。
  • 把空集合也算進去。
  • 位元運算的優先順序:mask & 1 << i 是 mask & (1 << i),可以用但容易誤讀,建議加括號或寫成 mask >> i & 1。

面試口說整理
#

OR 只會增加位元,所以最大的 OR 就是所有元素 OR 起來。接著用回溯枚舉每個元素選或不選,一旦目前的 OR 已經等於最大值,剩下的元素怎麼選都不會改變結果,直接加上 2^(剩下的個數)。n 最多 16,最壞 O(2^n)。

相關文章

LeetCode 473: Matchsticks to Square
·1 分鐘
LeetCode Medium Array Dynamic-Programming Backtracking Bit-Manipulation
LeetCode 473 解題筆記:總和要能被 4 整除;由大到小把每根火柴放進四個邊之一,超過邊長或跟前一個邊長度相同就跳過
LeetCode 78: Subsets
·1 分鐘
LeetCode Medium Array Backtracking Bit-Manipulation
LeetCode 78 解題筆記:每個元素選或不選;迭代寫法是每加入一個數,就把現有的每個子集合複製一份並加上它
LeetCode 90: Subsets II
·1 分鐘
LeetCode Medium Array Backtracking Bit-Manipulation
LeetCode 90 解題筆記:先排序;遇到跟前一個相同的數,只延伸上一步新產生的那些子集合
LeetCode 988: Smallest String Starting From Leaf
·1 分鐘
LeetCode Medium String Backtracking Tree Depth-First-Search
LeetCode 988 解題筆記:DFS 時把字元加在字串前面,到葉子時就是「從葉子到根」的字串,取最小的
LeetCode 113: Path Sum II
·1 分鐘
LeetCode Medium Backtracking Tree Depth-First-Search Binary-Tree
LeetCode 113 解題筆記:回溯記錄目前的路徑,到葉子時總和等於目標就存一份複本;值可能是負的,不能提早剪枝
LeetCode 222: Count Complete Tree Nodes
·1 分鐘
LeetCode Medium Binary-Search Bit-Manipulation Tree Binary-Tree
LeetCode 222 解題筆記:完全二元樹,左右兩邊的最左高度相同代表那一邊是滿的,直接用 2^h - 1;O(log² n)