给你一个整数数组 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
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
鼠鼠我啊,学艺不精,第一个入脑的就是暴力解,最后还是通过了。
这里的暴力不是完全的暴力,因为还是做了预处理,就是方法比较好懂而已。
首先观察,思考一下每种可能:
如果这个数组大小比2小怎么办。
如果遇到一个0怎么办。
如果遇到两个数按位与刚好能成的怎么办。
如果遇到三个数按位与能成怎么办。
先不看第一条。首先,遇到一个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;
}
};