【982】按位与为零的三元组

给你一个整数数组 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] < 216

来源:力扣(LeetCode)

链接:https://leetcode.cn/problems/triples-with-bitwise-and-equal-to-zero

著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

鼠鼠我啊,学艺不精,第一个入脑的就是暴力解,最后还是通过了。

这里的暴力不是完全的暴力,因为还是做了预处理,就是方法比较好懂而已。

首先观察,思考一下每种可能:

  1. 如果这个数组大小比2小怎么办。

  1. 如果遇到一个0怎么办。

  1. 如果遇到两个数按位与刚好能成的怎么办。

  1. 如果遇到三个数按位与能成怎么办。

先不看第一条。首先,遇到一个0,说明其它数是多少不重要。假设这个0是第i个数(从0开始计数),那么它能成的可能就有3*(l-i)*(l-i-1)+1种,+1的原因是三个自己也是能成的。其次,如果两个数刚好能成,则第三个数不重要。当第三个数不存在于这两个数之间时,一共有6*(l-i-1)种。当第三个数存在于这两个数之间时,一共有6种。所以第三条一共有6*(l-i)种。第四条很简单,三个数排列组合,基本问题,答案是6种。

按道理,这样加起来确实做过了预处理。

但我第一次没过。为什么呢?

有没有可能,如果全是第三种情况,按位与也是要时间的。记不记得算法课讲过,记录子问题的结果以减少计算时间?所以,我们仔细观察会发现,如果是第三种情况,其实它有两个数的按位与结果是已经算过的。所以我们要做的就是取一个数把它存下来。

这样就通过了。

class Solution {
public:
    int countTriplets(vector<int>& nums) {
        int l=nums.size();
        //0,3*pow(l,2),
        //如果两个数按位与是0,3*2*(l-1);
        //如果三个数按位与是0,6
        int cont=0;
        int i;
        for(i=0;i<l;++i){
            if(l-i<3){
                break;
            }
            if(nums[i]==0){
                cont=cont+3*(l-i)*(l-i-1)+1;
            }
            else{
                for(int j=i+1;j<l;++j){
                    int num=(nums[i]&nums[j]);
                    if(num==0){
                        cont=cont+6*(l-j);
                    }
                    else{
                        for(int k=j+1;k<l;++k){
                            if((num&nums[k])==0){
                                cont+=6;
                            }
                        }
                    }
                }
            }
        }
        if (l-i==2){
            if(nums[i]==0){
                cont+=7;
            }
            else if((nums[i]&nums[i+1])==0){
                cont+=6;
            }
            i++;
        }
        if(l-i==1){
            if(nums[i]==0){
                cont++;
            }
        }
        return cont;
    }
};

但感觉效率很低。

为什么呢。我也不知道为什么,感觉明明少算了很多,但从结果来看没有少算。是因为三层循环吗。需要大佬指点迷津。

学了另一种写法。遍历所有的两数组合,记录结果。然后遍历两数集合的所有结果,找出三个数也是0的组合。

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 x : nums)
            for (int y = 0; y < 1 << 16; ++y)
                if ((x & y) == 0)
                    ans += cnt[y];
        return ans;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值