LeetCode 982 - Triples with Bitwise AND Equal To Zero
This problem asks us to count the number of triples (i, j, k) from an integer array nums such that the bitwise AND of the three numbers at these indices equals zero. In other words, we want all combinations where nums[i] & nums[j] & nums[k] == 0.
Difficulty: 🔴 Hard
Topics: Array, Hash Table, Bit Manipulation
Solution
Problem Understanding
This problem asks us to count the number of triples (i, j, k) from an integer array nums such that the bitwise AND of the three numbers at these indices equals zero. In other words, we want all combinations where nums[i] & nums[j] & nums[k] == 0.
The input is an array of integers where each element ranges from 0 to 2^16 - 1 (i.e., 0 to 65535), and the length of the array is up to 1000. The output is a single integer representing the number of valid triples.
Edge cases to consider include arrays that contain only zeros, arrays with maximum values, or arrays with repeated elements. A naive implementation may struggle due to the potential number of combinations - for n = 1000, a straightforward triple-nested loop would result in a billion iterations, which is not feasible.
Approaches
The brute-force approach iterates over all possible triples (i, j, k) using three nested loops. For each triple, it computes the bitwise AND and checks if it equals zero. While this approach is simple and correct, it has a cubic time complexity of O(n^3), which is too slow for n = 1000.
The optimal approach relies on the observation that bitwise AND is associative and commutative. We can precompute all possible pairs (i, j) and store the frequency of their AND results in a hash map. For each number nums[k], we can then iterate over the keys of this map and check if key & nums[k] == 0. If so, we add the count from the map to the result. This reduces the time complexity significantly because we avoid iterating through all triples directly.
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^3) | O(1) | Triple nested loop over all elements |
| Optimal | O(n^2 * 2^16) in worst case | O(2^16) | Precompute pairwise ANDs and use a hash map for counts |
Algorithm Walkthrough
- Initialize a hash map to store the frequency of AND results of all pairs
(nums[i] & nums[j]). - Iterate through every pair
(i, j)in the array and computenums[i] & nums[j]. Increment the frequency of this value in the hash map. - Initialize a variable
resultto zero to store the count of valid triples. - Iterate through each number
nums[k]in the array. For each key in the hash map, check ifkey & nums[k] == 0. If true, add the corresponding frequency from the map toresult. - Return
result.
Why it works: Precomputing the pairwise ANDs and counting their frequencies ensures that for each potential third element, we efficiently sum all pairs that will produce zero when ANDed with it. This avoids the cubic iteration over all triples.
Python Solution
from typing import List
from collections import defaultdict
class Solution:
def countTriplets(self, nums: List[int]) -> int:
pair_count = defaultdict(int)
# Count all pairwise AND results
for i in nums:
for j in nums:
pair_count[i & j] += 1
result = 0
# For each number, sum all pair counts that AND to 0 with it
for num in nums:
for key, count in pair_count.items():
if key & num == 0:
result += count
return result
Explanation: We first build a dictionary pair_count to store how many times each AND result occurs among all pairs. Then, for each number in the array, we iterate over all keys in the dictionary and check if ANDing with this number gives zero. The sum of all such counts is our final result.
Go Solution
func countTriplets(nums []int) int {
pairCount := make(map[int]int)
for _, i := range nums {
for _, j := range nums {
pairCount[i & j]++
}
}
result := 0
for _, num := range nums {
for key, count := range pairCount {
if key & num == 0 {
result += count
}
}
}
return result
}
Explanation: The Go implementation mirrors the Python solution. We use a map pairCount to track pairwise AND frequencies. Unlike Python, Go requires explicit initialization of maps. Iteration and addition are handled in the same way.
Worked Examples
Example 1: nums = [2,1,3]
- Compute 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
pair_count= {2:3, 0:2, 1:3, 3:1}
- For each num:
- num=2 → keys that AND with 2 give 0 → key=0 → add 2 to result
- num=1 → keys that AND with 1 give 0 → key=0 → add 2 to result
- num=3 → keys that AND with 3 give 0 → key=0 → add 2 to result
- Continue for all keys → final
result = 12
Example 2: nums = [0,0,0]
- All pairwise ANDs = 0 →
pair_count = {0:9} - Each number AND 0 = 0 → add 9 each time for 3 numbers →
result = 27
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n^2 + n*uniquePairs) | Precomputing pairs is O(n^2). Iterating over map keys for each num is O(n*uniquePairs). In worst case, uniquePairs <= 2^16 |
| Space | O(2^16) | Storing pairwise AND counts in a hash map with keys up to 2^16 |
The main saving is that the pairwise map avoids iterating through all n^3 triples directly. In practice, the number of unique AND values is much smaller than the theoretical maximum.
Test Cases
# Provided examples
assert Solution().countTriplets([2,1,3]) == 12 # mixture of small numbers
assert Solution().countTriplets([0,0,0]) == 27 # all zeros
# Edge cases
assert Solution().countTriplets([1]) == 1 # single element
assert Solution().countTriplets([65535,65535]) == 0 # max values, no zero AND
assert Solution().countTriplets([0,1,2,3]) == 36 # small diverse set
assert Solution().countTriplets([5,1,0]) == 12 # zero guarantees many triples
| Test | Why |
|---|---|
[2,1,3] |
Small diverse set to check correct counting |
[0,0,0] |
Edge case where all numbers are zero |
[1] |
Minimum input size |
[65535,65535] |
Maximum values to test no valid triples |
[0,1,2,3] |
Mix of zero and small numbers |
[5,1,0] |
Zero allows multiple valid triples |
Edge Cases
Single-element array: A single-element array still forms a triple with repetition (i,j,k) all equal to 0, if the number is 0. The implementation correctly handles repeated indices.
Array with all zeros: Every combination of indices produces zero, leading to n^3 triples. Our algorithm handles this efficiently using the precomputed pair map.
Array with maximum numbers: Elements equal to 2^16 - 1 will rarely produce zero when ANDed. The algorithm correctly excludes these pairs by checking key & num == 0 during the final accumulation.
These edge cases ensure the algorithm correctly handles small inputs, repeated zeros, and maximum boundary values without errors or overcounting.