首页 > 其他分享 >982. 按位与为零的三元组 (Hard)

982. 按位与为零的三元组 (Hard)

时间:2023-03-04 15:47:48浏览次数:49  
标签:nums 982 Hard 三元组 按位 集合 子集

问题描述

982. 按位与为零的三元组 (Hard)

给你一个整数数组 nums ,返回其中 按位与三元组 的数目。

按位与三元组 是由下标 (i, j, k) 组成的三元组,并满足下述全部条件:

  • 0 <= i < nums.length
  • 0 <= j < nums.length
  • 0 <= k < nums.length
  • nums[i] & nums[j] & nums[k] == 0 ,其中 & 表示按位与运算符。

示例 1:

输入:nums = [2,1,3]
输出:12
解释:可以选出如下 i, j, k 三元组:
(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

示例 2:

输入:nums = [0,0,0]
输出:27

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] < 2¹⁶

解题思路

可以先用哈希表ump存储nums[i] & nums[j]的结果,再与nums[k]按位与,如果结果为0,res += ump[nums[i] & nums[j]]

优化,我们把二进制看成集合,二进制从低到高第i位为1表示i在集合中,为0表示i不在集合中,例如\(a = 1101_{(2)}\)表示集合\(A=\{0,2,3\}\);

那么,\(a \& b = 0\)即集合\(A\)和集合\(B\)没有交集,或者说\(B\)是集合\(\complement_U A\),这里\(U=\{0,1,2,...,15\}\),对应的数字即\(0xffff\),一个数异或\(0xffff\)就能得到这个数的补集;

所以,代码可以优化成枚举\(m = nums[k]\oplus 0xffff\)的子集;

要枚举\(m\)的子集\(s\),可以将\(s\)从\(m\)不断减一直到0,如果\(s \& m = s\),说明\(s\)是\(m\)的子集;

更高效的做法是直接“跳到”下一个子集,即\(s\)更新为\((s - 1)\& m\),这样做的正确性在于,\(s-1\)只把\(s\)最低位的\(1\)改成了\(0\)(考虑\(s\)对应的集合),比这个\(1\)更低的\(0\)全都变成了\(1\),因此下一个子集,一定是\(s-1\)的子集,直接\(\&m\),就能得到下一个子集;

最后,当\(s=0\)时,由于\(-1\)的二进制全为\(1\),所有\((s-1)\&m = m\),所以我们可以判断下一个子集是否又回到\(m\),来判断是否要退出循环。

注:这一技巧常用于子集状态压缩DP中

代码

class Solution {
public:
    int countTriplets(vector<int> &nums) {
        int cnt[1 << 16]{};
        for (int x : nums)
            for (int y : nums)
                ++cnt[x & y];
        int ans = 0;
        for (int m : nums) {
            m ^= 0xffff;
            int s = m;
            do { // 枚举 m 的子集(包括空集)
                ans += cnt[s];
                s = (s - 1) & m;
            } while (s != m);
        }
        return ans;
    }
};

标签:nums,982,Hard,三元组,按位,集合,子集
From: https://www.cnblogs.com/zwyyy456/p/17178391.html

相关文章

  • 982. 安位与为 0 的三元组
    题目链接: 982.按位与为零的三元组-力扣(LeetCode)    按位与为零的三元组-按位与为零的三元组-力扣(LeetCode) ......
  • 力扣---982. 按位与为零的三元组
    给你一个整数数组nums,返回其中按位与三元组的数目。按位与三元组是由下标(i,j,k)组成的三元组,并满足下述全部条件:   0<=i<nums.length   0<=j<nu......
  • 982. 按位与为零的三元组(位运算)
    题目https://leetcode.cn/problems/triples-with-bitwise-and-equal-to-zero/description/?orderBy=most_votes思路参考灵神题解,非常易懂,总结一些方法状态压缩常用......
  • LEETCODE 982. 按位与为零的三元组
    这题暴力的话会超时,考虑用哈希表来存储前两位与的结果的数量然后在另一个循环中枚举第三位和哈希表每个下标相与,找到结果为0的,对应的哈希表值加入ans中classSolution{publ......
  • 2023.3.4Leecode982按位与为零的三元组
    题目的要求给你一个整数数组nums,返回其中按位与三元组的数目。按位与三元组是由下标(i,j,k)组成的三元组,并满足下述全部条件:0<=i<nums.length0<=j<num......
  • 1223. 掷骰子模拟 (Hard)
    问题描述1223.掷骰子模拟(Hard)有一个骰子模拟器会每次投掷的时候生成一个1到6的随机数。不过我们在使用它时有个约束,就是使得投掷骰子时,连续掷出数字i的次数......
  • 552. 学生出勤记录 II (Hard)
    问题描述552.学生出勤记录II(Hard)可以用字符串表示一个学生的出勤记录,其中的每个字符用来标记当天的出勤情况(缺勤、迟到、到场)。记录中只含下面三种字符:'A':Absent,......
  • C# MongDb踩坑记录--分片写入报错 An upsert on a sharded collection must contain t
    环境C#mongdb.drivercollection分片_id为片键报错信息Awriteoperationresultedinanerror.WriteError:{Category:"Uncategorized",Code:61,Message:......
  • 403. 青蛙过河 (Hard)
    问题描述403.青蛙过河(Hard)一只青蛙想要过河。假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。青蛙可以跳上石子,但是不可以......
  • 312. 戳气球 (Hard)
    问题描述312.戳气球(Hard)有n个气球,编号为0到n-1,每个气球上都标有一个数字,这些数字存在数组nums中。现在要求你戳破所有的气球。戳破第i个气球,你可以获得......