You are given an integer array nums
. Your task is to count the number of triplets (i, j, k)
such that:
0 ≤ i, j, k < nums.length
nums[i]
, nums[j]
, and nums[k]
is equal to 0
(i.e., nums[i] & nums[j] & nums[k] == 0
).i
, j
, and k
can be equal or different.
At first glance, the problem seems to require checking all possible triplets in the array. For each combination of i
, j
, and k
, we would compute nums[i] & nums[j] & nums[k]
and see if the result is zero.
However, with up to 1000 elements, there are up to 1,000,000,000 (109) possible triplets, which is far too many for a brute-force approach.
We need to find a way to avoid checking every triplet individually. This leads us to consider whether we can count the number of valid pairs or partial results in advance, and use that to speed up the solution.
The key insight is that the bitwise AND operation is associative and commutative, and the constraints on the numbers are small (16 bits), so we can use this to precompute and optimize.
We'll break down the optimized algorithm step by step:
(a, b)
in nums
, compute nums[a] & nums[b]
.c
in nums
, and for each possible value x
(the result of nums[a] & nums[b]
), if (x & c) == 0
, then all pairs (a, b)
with AND x
can form a valid triplet with c
.x
and c
combinations.
Let's walk through an example with nums = [2, 1, 3]
.
Step 1: Compute all pairwise ANDs
2 & 2 = 2
2 & 1 = 0
2 & 3 = 2
1 & 2 = 0
1 & 1 = 1
1 & 3 = 1
3 & 2 = 2
3 & 1 = 1
3 & 3 = 3
c = 2
: Which AND results can form 0 with 2?0 & 2 = 0
(yes), 1 & 2 = 0
(yes), 2 & 2 = 2
(no), 3 & 2 = 2
(no).c = 2
, valid counts are those for 0 and 1: 2 + 3 = 5
c = 1
and c = 3
similarly:c = 1
: 0 & 1 = 0
(yes), 1 & 1 = 1
(no), 2 & 1 = 0
(yes), 3 & 1 = 1
(no).c = 1
are for 0 and 2: 2 + 3 = 5
c = 3
: 0 & 3 = 0
(yes), 1 & 3 = 1
(yes), 2 & 3 = 2
(yes), 3 & 3 = 3
(no).c = 3
are for 0, 1, 2: 2 + 3 + 3 = 8
5 + 5 + 8 = 18
valid triplets.
Brute-force approach:
from collections import Counter
class Solution:
def countTriplets(self, nums):
pair_count = Counter()
for a in nums:
for b in nums:
pair_count[a & b] += 1
result = 0
for c in nums:
for x in pair_count:
if (x & c) == 0:
result += pair_count[x]
return result
#include <vector>
#include <unordered_map>
using namespace std;
class Solution {
public:
int countTriplets(vector<int>& nums) {
unordered_map<int, int> pair_count;
int n = nums.size();
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
pair_count[nums[i] & nums[j]]++;
}
}
int result = 0;
for (int k = 0; k < n; ++k) {
for (auto& [x, cnt] : pair_count) {
if ((x & nums[k]) == 0) {
result += cnt;
}
}
}
return result;
}
};
import java.util.*;
class Solution {
public int countTriplets(int[] nums) {
Map<Integer, Integer> pairCount = new HashMap<>();
int n = nums.length;
for (int a : nums) {
for (int b : nums) {
int x = a & b;
pairCount.put(x, pairCount.getOrDefault(x, 0) + 1);
}
}
int result = 0;
for (int c : nums) {
for (Map.Entry<Integer, Integer> entry : pairCount.entrySet()) {
if ((entry.getKey() & c) == 0) {
result += entry.getValue();
}
}
}
return result;
}
}
var countTriplets = function(nums) {
const pairCount = new Map();
for (let a of nums) {
for (let b of nums) {
const x = a & b;
pairCount.set(x, (pairCount.get(x) || 0) + 1);
}
}
let result = 0;
for (let c of nums) {
for (let [x, cnt] of pairCount.entries()) {
if ((x & c) === 0) {
result += cnt;
}
}
}
return result;
};
The key to efficiently solving this problem is leveraging the limited number of possible bitwise AND results due to the 16-bit constraint. By precomputing all possible pairwise ANDs and counting them, we can quickly determine for each number how many pairs it can form a valid triplet with. This turns a cubic-time brute-force problem into a much faster solution, making it feasible for large input sizes and demonstrating the power of precomputation and bit manipulation.