LeetCode每日一题(982. Triples with Bitwise AND Equal To Zero)

给定一个整数数组,文章讲述了如何计算满足特定位AND条件的三元组(i,j,k)的数量。通过使用位运算,统计每个数字对的AND结果的频率,并检查哪些数字与数组中的其他数字进行AND操作结果为0,从而得出答案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Given an integer array nums, return the number of AND triples.

An AND triple is a triple of indices (i, j, k) such that:

0 <= i < nums.length
0 <= j < nums.length
0 <= k < nums.length
nums[i] & nums[j] & nums[k] == 0, where & represents the bitwise-AND operator.

Example 1:

Input: nums = [2,1,3]
Output: 12

Explanation: We could choose the following i, j, k triples:
(i=0, j=0, k=1) : 2 & 2 & 1
(i=0, j=1, k=0) : 2 & 1 & 2
(i=0, j=1, k=1) : 2 & 1 & 1
(i=0, j=1, k=2) : 2 & 1 & 3
(i=0, j=2, k=1) : 2 & 3 & 1
(i=1, j=0, k=0) : 1 & 2 & 2
(i=1, j=0, k=1) : 1 & 2 & 1
(i=1, j=0, k=2) : 1 & 2 & 3
(i=1, j=1, k=0) : 1 & 1 & 2
(i=1, j=2, k=0) : 1 & 3 & 2
(i=2, j=0, k=1) : 3 & 2 & 1
(i=2, j=1, k=0) : 3 & 1 & 2

Example 2:

Input: nums = [0,0,0]
Output: 27

Constraints:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] < 2^16

因为 nums[i] < 2 ^ 16, 所以 nums[i] & nums[j] < 2 ^ 16, 我们用一个长度为 2 ^ 16 的数组 counts 来保存每个 nums[i] & nums[j]的出现频次,假设 n 为 0 到 2^16 中的每个数字, 如果 nums[i] & n == 0, 则 ans += counts[n]



impl Solution {
    pub fn count_triplets(nums: Vec<i32>) -> i32 {
        let mut counts = vec![0; 1 << 16];
        for i in 0..nums.len() {
            for j in 0..nums.len() {
                counts[(nums[i] & nums[j]) as usize] += 1;
            }
        }
        let mut ans = 0;
        for i in 0..nums.len() {
            for j in 0..1 << 16 {
                if nums[i] & j == 0 {
                    ans += counts[j as usize];
                }
            }
        }
        ans
    }
}

<think>好的,我现在需要帮助用户理解这段代码:return (n & k) != k ? -1 : __builtin_popcount(n ^ k);。用户可能是在学习位运算或者遇到了相关问题,需要解析这段代码的逻辑和作用。 首先,我应该拆解这段代码的各个部分。用户可能对位运算符和内置函数不太熟悉,所以需要逐步解释。代码中有两个主要部分:条件判断和三目运算符,然后是内置函数__builtin_popcount的使用。 先看条件部分(n & k) != k。这里用到了位与操作,判断n和k的按位与结果是否不等于k。如果不等,返回-1;否则,计算n异或k的二进制中1的个数。 接下来需要理解这个条件的意义。按位与操作的结果如果等于k,说明k的每一位1在n中对应的位置也是1。也就是说,k是n的一个子集。比如,如果k是二进制101,那么n在第三位和第一位必须都是1才能满足条件。如果不符合,说明无法满足,返回-1。 然后是异或操作n ^ k。异或的特性是相同为0,不同为1。所以n和k异或后,结果为1的位就是两者不同的地方。但由于之前已经通过条件判断确保k的所有1位在n中都是1,所以异或后的结果中,k的1位在n中都是1,因此这些位的异或会是0。而n中可能还有其他1位是k中没有的,所以异或后的1位就是n中存在但k中不存在的那些位。 __builtin_popcount函数是GCC内置函数,用于计算一个整数二进制中1的个数。所以这里的结果是统计n比k多出的1的位数。这可能是在计算需要翻转的位数,或者某种差异度量。 用户可能的疑问包括:这段代码的应用场景是什么?比如,可能用于检查k是否是n的子集,并计算差异位数。或者是在某些位操作问题中,比如找出需要改变多少位才能从n得到k,但前提是k是n的子集。 同时需要考虑到用户可能对补码、位运算不熟悉,需要举例说明。例如,当n=13(1101),k=5(0101)时,n & k=0101=k,所以条件成立,异或后得到1000,有1个1,返回1。这表示n比k多一个1位。 但若n=10(1010),k=5(0101),则n & k=0000≠k,返回-1,说明k的1位在n中没有全部出现。 用户可能在实际编程中遇到类似问题,比如在解决位掩码相关的问题,或者在算法题中需要这样的位操作技巧。需要指出这段代码可能的应用场景,如判断是否满足某些位条件并计算差异,或者在优化问题中快速计算某些指标。 另外,用户可能想知道为什么使用__builtin_popcount而不是手动计算,这时候需要解释内置函数的高效性,通常编译器会优化为底层指令,速度更快。 最后,总结这段代码的作用:首先检查k的所有设置位是否在n中也设置,如果是,则返回n和k不同位的数量(即需要翻转的位数),否则返回-1。</think>### 一、代码功能解析 这段代码的核心功能是:**验证二进制数$k$是否是$n$的子集**,若是则返回$n$与$k$不同二进制位的数量,否则返回$-1$。 --- ### 二、逐层拆解分析 #### 1. 条件判断 `(n & k) != k` - **位运算逻辑**: - `n & k` 会对$n$和$k$的二进制位进行按位与操作 - **关键性质**:只有当$k$的某一位是$1$时,$n$的对应位也必须是$1$,否则`(n & k)`的结果会小于$k$ - **数学表达**:若$k \subseteq n$(二进制位包含关系),则 $(n \mathbin{\&} k) = k$ #### 2. 三目运算符 `? -1 : ...` - **逻辑说明**: - 若$k$不是$n$的子集(即条件为真),返回$-1$ - 若$k$是$n$的子集(即条件为假),执行`__builtin_popcount(n ^ k)` #### 3. 差异位计算 `__builtin_popcount(n ^ k)` - **异或操作**: - `n ^ k` 会标记所有不同的二进制位(相同为$0$,不同为$1$) - 由于已确保$k \subseteq n$,差异位只能是$n$中存在但$k$中不存在的$1$ - **内置函数**: - `__builtin_popcount()` 是GCC编译器内置函数,用于统计二进制中$1$的个数 - **数学意义**:计算$n$比$k$多出的$1$的个数 --- ### 三、示例演示 #### 例1:$n = 13$(二进制`1101`),$k = 5$(二进制`0101`) 1. `n & k = 0101`,等于$k$ → 条件为假 2. `n ^ k = 1000`,结果中有1个$1$ 3. **最终输出**:$1$ #### 例2:$n = 10$(二进制`1010`),$k = 5$(二进制`0101`) 1. `n & k = 0000`,不等于$k$ → 条件为真 2. **直接返回**:$-1$ --- ### 四、应用场景 1. **位掩码验证**:检测某个权限组合是否完全包含在另一个权限集中 2. **硬件寄存器**:验证配置位的兼容性 3. **算法题应用**:如LeetCode 982. Triples with Bitwise AND Equal to Zero --- ### 五、代码优化说明 | 操作 | 时间复杂度 | 空间复杂度 | |---------------------|------------|------------| | 位运算判断 | $O(1)$ | $O(1)$ | | `__builtin_popcount` | $O(1)$* | $O(1)$ | > 注:`__builtin_popcount`实际通过CPU指令`POPCNT`实现,效率极高 --- ### 六、扩展思考 尝试用以下方式改写代码: 1. 用标准库`<bitset>`替代`__builtin_popcount` 2. 实现手动计算差异位数(通过移位操作) 3. 处理$n$和$k$为负数时的补码问题 **手动计算差异位示例**: ```cpp int countBits(int x) { int count = 0; while (x) { count += x & 1; x >>= 1; } return count; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值