2044. 统计按位或能得到最大值的子集数目

【算法题解】统计按位或最大值子集的数量 —— 子集按位或最大值计数问题


题目描述.

给定一个整数数组 nums,请你找出 nums 所有非空子集按位或(bitwise OR)可能得到的最大值,并返回按位或能得到最大值的不同非空子集的数目。

  • 子集定义:如果数组 a 可以由数组 b 删除一些元素(或不删除)得到,则认为 ab 的一个子集。
  • 子集不同定义:如果选中的元素下标位置不一样,则认为两个子集不同。
  • 按位或定义:对于数组 a,其按位或为 a[0] OR a[1] OR ... OR a[a.length-1]

输入输出示例

示例 1:

输入:nums = [3,1]
输出:2

解释:
所有非空子集的按位或值:
- [3] -> 3
- [1] -> 1
- [3,1] -> 3 OR 1 = 3

最大按位或值是3,能得到3的子集有2个:[3], [3,1]

示例 2:

输入:nums = [2,2,2]
输出:7

解释:
所有非空子集的按位或均为2,总共有 2^3 - 1 = 7 个非空子集

示例 3:

输入:nums = [3,2,1,5]
输出:6

解释:
最大按位或值为7,能得到7的子集有6个:
- [3,5]
- [3,1,5]
- [3,2,5]
- [3,2,1,5]
- [2,5]
- [2,1,5]

解题分析

本题核心在于:

  1. 求所有非空子集按位或的最大值 max_or
  2. 统计所有按位或等于 max_or 的子集个数

题目对数组长度最大为16,意味着可以考虑用穷举所有子集的方法。

子集数量

  • 数组长度为 n,非空子集数量为 2^n - 1
  • 最大16长度时,子集数量约为 65535,枚举可接受。

按位或的性质

  • 按位或是单调递增的:子集包含更多元素时按位或值只会变大或保持不变。
  • 不需要特别排序,只需遍历所有子集,计算按位或。

解题方法

方法一:位掩码枚举

使用位掩码从 1(1 << n) - 1 遍历所有非空子集。

  • 对每个子集,计算按位或。
  • 维护最大按位或值 max_or
  • 统计按位或等于 max_or 的子集数量。

代码实现(Python)

from typing import List

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

        # 遍历所有非空子集
        for mask in range(1, 1 << n):
            or_val = 0
            for i in range(n):
                if mask & (1 << i):
                    or_val |= nums[i]
            # 更新最大按位或及计数
            if or_val > max_or:
                max_or = or_val
                count = 1
            elif or_val == max_or:
                count += 1

        return count

复杂度分析

  • 时间复杂度:O(n×2n)O(n \times 2^n),
    • 枚举子集 2n2^n 个,每个子集最多计算 nn 个元素按位或。
  • 空间复杂度:O(1)O(1) 额外空间。

对于 n≤16n \leq 16,性能足够。


示例说明

nums = [3,2,1,5] 为例:

  • 枚举非空子集,计算按位或:
    • 子集 [3,5],按位或为 3 | 5 = 7
    • 子集 [3,1,5],按位或为 3 | 1 | 5 = 7
  • 找出最大按位或 7,统计有多少子集按位或等于 7,答案为 6

方法扩展与优化

  • 回溯剪枝:可以用回溯递归,从左到右决定选或不选某元素,动态维护当前按位或值。遇到已经达到最大可能按位或值时,可以提前剪枝。
  • 预计算整体按位或上界:先求整个数组的按位或 total_or,作为最大值的上限。
  • 存储中间结果:若题目改成要求返回所有最大按位或子集的集合,可能需要存储对应子集。

总结

本题利用位掩码枚举所有非空子集,计算其按位或,并统计最大按位或子集数量,解法直接且高效。

对于限制在16的规模,枚举全部子集完全可行,且代码易于理解和实现。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值