982. 按位与为零的三元组
牢记,当暴力铁超时的时候,尝试找规律或者记录中间结果
首先,数据量是 1 < = n u m s . l e n g t h < = 1000 1 <= nums.length <= 1000 1<=nums.length<=1000,如果三重循环枚举,铁定超时,那怎么办呢,尝试找规律。(这题找不到规律,不想看分析就直接跳过)
三个数字按位与的话,如果已经有两个数字按位与为0了,那第三个数字是什么都无所谓,这个组合可以与数组中的任何一个数字组成按位与为0的三元组。同时,第三个数字如果与这两个数字在数组中的下标互异,那这三个数字有三种组合方式;如果其中有两个数字下标相同,那么只有两种组合。可是问题来了,如果必须是三个数字都参与才能按位与为0呢,比如 1 、 2 、 4 1、2、4 1、2、4,这种情况还是只能通过枚举来找,所以找规律行不通,还是得暴力。
然后我们注意到 0 < = n u m s [ i ] < 2 16 0<=nums[i] < 2^{16} 0<=nums[i]<216,很容易想到,任何一个 n u m s [ i ] & n u m s [ j ] nums[i]\&nums[j] nums[i]&nums[j]的结果依然在这个范围,因此,我们可以先两层循环,枚举两个数按位与能得到的所有结果的数量,用数组或者哈希表存一下,然后再一个两层循环,枚举哈希表里的数和原始数组里的数按位与,如果为0,则加上哈希表中的这个数的数量。最后的结果即是要求的组合数。
class Solution {
public:
int countTriplets(vector<int>& nums) {
map<int,int> cnt;
int ans=0;
for(auto i:nums){
for(auto j:nums){
cnt[i&j]++;
}
}
for(auto k:nums){
for(auto &v:cnt){
if((k&v.first)==0){
ans+=v.second;
}
}
}
return ans;
}
};
然后看了题解,发现还有优化空间。整体做法一致,优化在于遍历方式。具体看灵神题解。