🧠
Optimal: Count Pairs With XOR in [low, high] Using Bit Trie
💡 This approach uses the previous method twice to count pairs with XOR less than or equal to high and less than low, then subtracts to get pairs in [low, high]. It combines bit trie queries with prefix counting for optimal performance.
Intuition
Number of pairs with XOR in [low, high] = count pairs with XOR < high+1 - count pairs with XOR < low.
Algorithm
- Implement a function to count pairs with XOR less than a given limit using a bit trie (from Approach 2).
- Compute countHigh = countPairsLessThan(nums, high + 1).
- Compute countLow = countPairsLessThan(nums, low).
- Return countHigh - countLow as the number of pairs with XOR in [low, high].
💡 This subtraction trick avoids complex range queries and leverages the efficient counting method for simpler bounds.
class TrieNode:
def __init__(self):
self.children = [None, None]
self.count = 0
class Solution:
def countPairsLessThan(self, nums, limit):
root = TrieNode()
def insert(num):
node = root
for i in range(20, -1, -1):
bit = (num >> i) & 1
if not node.children[bit]:
node.children[bit] = TrieNode()
node = node.children[bit]
node.count += 1
def query(num, limit):
node = root
count = 0
for i in range(20, -1, -1):
if not node:
break
bit_num = (num >> i) & 1
bit_lim = (limit >> i) & 1
if bit_lim == 1:
if node.children[bit_num]:
count += node.children[bit_num].count
node = node.children[1 - bit_num]
else:
node = node.children[bit_num]
return count
result = 0
for num in nums:
result += query(num, limit)
insert(num)
return result
def countPairs(self, nums, low, high):
return self.countPairsLessThan(nums, high + 1) - self.countPairsLessThan(nums, low)
# Driver code
if __name__ == '__main__':
nums = [1,4,2,7]
low, high = 2, 6
sol = Solution()
print(sol.countPairs(nums, low, high)) # Output: 6
Line Notes
class TrieNode:Defines trie node structure
def countPairsLessThan(self, nums, limit):Counts pairs with XOR less than limit
for i in range(20, -1, -1):Traverse bits from MSB to LSB
if bit_lim == 1:Add counts from matching subtree when limit bit is 1
result += query(num, limit)Accumulate count for each number
def countPairs(self, nums, low, high):Final function to count pairs in [low, high]
return self.countPairsLessThan(nums, high + 1) - self.countPairsLessThan(nums, low)Subtract counts to get pairs in range
class TrieNode {
TrieNode[] children = new TrieNode[2];
int count = 0;
}
public class Solution {
TrieNode root = new TrieNode();
void insert(int num) {
TrieNode node = root;
for (int i = 20; i >= 0; i--) {
int bit = (num >> i) & 1;
if (node.children[bit] == null) {
node.children[bit] = new TrieNode();
}
node = node.children[bit];
node.count++;
}
}
int query(int num, int limit) {
TrieNode node = root;
int count = 0;
for (int i = 20; i >= 0; i--) {
if (node == null) break;
int bitNum = (num >> i) & 1;
int bitLim = (limit >> i) & 1;
if (bitLim == 1) {
if (node.children[bitNum] != null) {
count += node.children[bitNum].count;
}
node = node.children[1 - bitNum];
} else {
node = node.children[bitNum];
}
}
return count;
}
int countPairsLessThan(int[] nums, int limit) {
root = new TrieNode();
int result = 0;
for (int num : nums) {
result += query(num, limit);
insert(num);
}
return result;
}
public int countPairs(int[] nums, int low, int high) {
return countPairsLessThan(nums, high + 1) - countPairsLessThan(nums, low);
}
public static void main(String[] args) {
Solution sol = new Solution();
int[] nums = {1,4,2,7};
int low = 2, high = 6;
System.out.println(sol.countPairs(nums, low, high)); // Output: 6
}
}
Line Notes
TrieNode[] children = new TrieNode[2];Children for bits 0 and 1
int count = 0;Count of numbers passing through node
for (int i = 20; i >= 0; i--)Traverse bits from MSB to LSB
if (bitLim == 1)Add counts from matching subtree when limit bit is 1
result += query(num, limit);Accumulate count for each number
int countPairs(int[] nums, int low, int high)Final function to count pairs in range
return countPairsLessThan(nums, high + 1) - countPairsLessThan(nums, low);Subtract counts to get pairs in [low, high]
#include <iostream>
#include <vector>
using namespace std;
struct TrieNode {
TrieNode* children[2] = {nullptr, nullptr};
int count = 0;
};
class Solution {
TrieNode* root;
public:
Solution() {
root = new TrieNode();
}
void insert(int num) {
TrieNode* node = root;
for (int i = 20; i >= 0; i--) {
int bit = (num >> i) & 1;
if (!node->children[bit]) {
node->children[bit] = new TrieNode();
}
node = node->children[bit];
node->count++;
}
}
int query(int num, int limit) {
TrieNode* node = root;
int count = 0;
for (int i = 20; i >= 0; i--) {
if (!node) break;
int bitNum = (num >> i) & 1;
int bitLim = (limit >> i) & 1;
if (bitLim == 1) {
if (node->children[bitNum]) {
count += node->children[bitNum]->count;
}
node = node->children[1 - bitNum];
} else {
node = node->children[bitNum];
}
}
return count;
}
int countPairsLessThan(vector<int>& nums, int limit) {
root = new TrieNode();
int result = 0;
for (int num : nums) {
result += query(num, limit);
insert(num);
}
return result;
}
int countPairs(vector<int>& nums, int low, int high) {
return countPairsLessThan(nums, high + 1) - countPairsLessThan(nums, low);
}
};
int main() {
vector<int> nums = {1,4,2,7};
int low = 2, high = 6;
Solution sol;
cout << sol.countPairs(nums, low, high) << endl; // Output: 6
return 0;
}
Line Notes
TrieNode* children[2] = {nullptr, nullptr};Children pointers for bits 0 and 1
int count = 0;Count of numbers passing through node
for (int i = 20; i >= 0; i--)Traverse bits from MSB to LSB
if (bitLim == 1)Add counts from matching subtree when limit bit is 1
result += query(num, limit);Accumulate count for each number
int countPairs(vector<int>& nums, int low, int high)Final function to count pairs in range
return countPairsLessThan(nums, high + 1) - countPairsLessThan(nums, low);Subtract counts to get pairs in [low, high]
class TrieNode {
constructor() {
this.children = [null, null];
this.count = 0;
}
}
class Solution {
constructor() {
this.root = new TrieNode();
}
insert(num) {
let node = this.root;
for (let i = 20; i >= 0; i--) {
let bit = (num >> i) & 1;
if (!node.children[bit]) {
node.children[bit] = new TrieNode();
}
node = node.children[bit];
node.count++;
}
}
query(num, limit) {
let node = this.root;
let count = 0;
for (let i = 20; i >= 0; i--) {
if (!node) break;
let bitNum = (num >> i) & 1;
let bitLim = (limit >> i) & 1;
if (bitLim === 1) {
if (node.children[bitNum]) {
count += node.children[bitNum].count;
}
node = node.children[1 - bitNum];
} else {
node = node.children[bitNum];
}
}
return count;
}
countPairsLessThan(nums, limit) {
this.root = new TrieNode();
let result = 0;
for (let num of nums) {
result += this.query(num, limit);
this.insert(num);
}
return result;
}
countPairs(nums, low, high) {
return this.countPairsLessThan(nums, high + 1) - this.countPairsLessThan(nums, low);
}
}
// Driver code
const nums = [1,4,2,7];
const low = 2, high = 6;
const sol = new Solution();
console.log(sol.countPairs(nums, low, high)); // Output: 6
Line Notes
this.children = [null, null];Children array for bits 0 and 1
this.count = 0;Count of numbers passing through node
for (let i = 20; i >= 0; i--)Traverse bits from MSB to LSB
if (bitLim === 1)Add counts from matching subtree when limit bit is 1
result += this.query(num, limit);Accumulate count for each number
countPairs(nums, low, high)Final function to count pairs in range
return this.countPairsLessThan(nums, high + 1) - this.countPairsLessThan(nums, low);Subtract counts to get pairs in [low, high]
TimeO(n * log M)
SpaceO(n * log M)
Two calls to countPairsLessThan each take O(n log M), total O(n log M). Space for trie is also O(n log M).
💡 This is efficient for large inputs, making it suitable for interview coding.
Interview Verdict: Accepted
This is the optimal approach combining bit trie and prefix counting to solve the problem efficiently.