2354. Number of Excellent Pairs
Problem Description
You have an array of positive integers nums
and a positive integer k
. Your task is to count pairs of numbers from the array that meet a special condition called "excellent."
A pair (num1, num2)
is considered excellent if:
- Both
num1
andnum2
come from the arraynums
- When you calculate
num1 OR num2
(bitwise OR) andnum1 AND num2
(bitwise AND), then count the total number of set bits (1s) in both results, this sum must be at leastk
For example, if num1 = 5
(binary: 101) and num2 = 3
(binary: 011):
num1 OR num2 = 7
(binary: 111) has 3 set bitsnum1 AND num2 = 1
(binary: 001) has 1 set bit- Total set bits = 3 + 1 = 4
The pairs (a, b)
and (c, d)
are considered different if either the first elements are different (a != c
) or the second elements are different (b != d
). This means (1, 2)
and (2, 1)
count as two distinct pairs.
You can also form a pair with the same number twice, like (num1, num1)
, as long as that number appears at least once in the array.
Your goal is to return the total count of all distinct excellent pairs.
The solution leverages a key mathematical property: the count of set bits in (A OR B)
plus the count of set bits in (A AND B)
equals the count of set bits in A
plus the count of set bits in B
. This allows the problem to be simplified by first removing duplicates from the array, counting the set bits for each unique number, and then checking all possible pairs to see if their combined bit counts meet or exceed k
.
Intuition
The key insight comes from understanding a fundamental property of bitwise operations. When we look at num1 OR num2
and num1 AND num2
, we're essentially examining how the bits of these two numbers interact.
Consider what happens at each bit position:
- If both numbers have a 1 at that position: OR gives 1, AND gives 1 (total: 2 bits)
- If only one number has a 1 at that position: OR gives 1, AND gives 0 (total: 1 bit)
- If both numbers have a 0 at that position: OR gives 0, AND gives 0 (total: 0 bits)
This reveals that the total count of set bits in (num1 OR num2) + (num1 AND num2)
is exactly equal to the count of set bits in num1
plus the count of set bits in num2
. Mathematically: bitcount(A OR B) + bitcount(A AND B) = bitcount(A) + bitcount(B)
.
This transformation is powerful because it converts our problem from checking complex bitwise operations for every pair to simply checking if the sum of bit counts of two numbers is at least k
.
Since we only care about the bit counts and not the actual values for pairing, we can:
- First remove duplicates from the array (using a set) since duplicate values would have the same bit count
- Count how many numbers have each possible bit count value
- For each unique number, check all possible bit count values and add up how many valid pairs can be formed
The solution avoids checking all n²
pairs directly by grouping numbers by their bit counts, making it more efficient. For each number with bit count t
, we look for all numbers whose bit count i
satisfies t + i >= k
, and add the count of such numbers to our answer.
Learn more about Binary Search patterns.
Solution Approach
The implementation follows these steps:
-
Remove duplicates: Convert the input array
nums
to a sets
. This eliminates duplicate values since a number paired with itself will always produce the same result regardless of how many times it appears in the original array. -
Count bit frequencies: Create a Counter dictionary
cnt
to track how many unique numbers have each specific bit count. We iterate through each unique numberv
in the set and incrementcnt[v.bit_count()]
. For example, if we have numbers 3 (binary: 11, 2 bits) and 5 (binary: 101, 2 bits), thencnt[2] = 2
. -
Count valid pairs: For each unique number
v
in the set:- Calculate its bit count
t = v.bit_count()
- Iterate through all entries
(i, x)
in the counter wherei
is a bit count value andx
is how many numbers have that bit count - If
t + i >= k
, it means this numberv
can form excellent pairs with allx
numbers that have bit counti
- Add
x
to the answer
- Calculate its bit count
The algorithm works because:
- When we pair a number with bit count
t
with any number having bit counti
, the condition for excellence becomest + i >= k
- Each number in the set is considered as the first element of a pair
- For each such number, we count how many valid second elements exist based on bit count requirements
- This automatically handles both
(a, b)
and(b, a)
as distinct pairs since each number gets a turn being the first element
Time Complexity: O(n × log(max_value))
where n
is the number of unique elements and log(max_value)
represents the maximum possible bit count.
Space Complexity: O(n)
for storing the set and counter.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with nums = [1, 2, 3, 1]
and k = 3
.
Step 1: Remove duplicates
- Convert
nums
to a set:s = {1, 2, 3}
- This removes the duplicate
1
since pairing with the same value always gives the same result
Step 2: Count bit frequencies
- For each unique number, count its set bits:
1
(binary: 001) has 1 set bit2
(binary: 010) has 1 set bit3
(binary: 011) has 2 set bits
- Create counter:
cnt = {1: 2, 2: 1}
- This means 2 numbers have 1 set bit, and 1 number has 2 set bits
Step 3: Count valid pairs
-
For
v = 1
(bit countt = 1
):- Check bit count 1: Since
1 + 1 = 2 < 3
, skip - Check bit count 2: Since
1 + 2 = 3 >= 3
, addcnt[2] = 1
to answer - Pairs formed:
(1, 3)
- Check bit count 1: Since
-
For
v = 2
(bit countt = 1
):- Check bit count 1: Since
1 + 1 = 2 < 3
, skip - Check bit count 2: Since
1 + 2 = 3 >= 3
, addcnt[2] = 1
to answer - Pairs formed:
(2, 3)
- Check bit count 1: Since
-
For
v = 3
(bit countt = 2
):- Check bit count 1: Since
2 + 1 = 3 >= 3
, addcnt[1] = 2
to answer - Check bit count 2: Since
2 + 2 = 4 >= 3
, addcnt[2] = 1
to answer - Pairs formed:
(3, 1)
,(3, 2)
,(3, 3)
- Check bit count 1: Since
Total answer: 1 + 1 + 3 = 5
The excellent pairs are: (1, 3)
, (2, 3)
, (3, 1)
, (3, 2)
, (3, 3)
We can verify: For pair (1, 3)
:
1 OR 3 = 3
(binary: 011) has 2 set bits1 AND 3 = 1
(binary: 001) has 1 set bit- Total: 2 + 1 = 3 >= k ✓
Solution Implementation
1class Solution:
2 def countExcellentPairs(self, nums: List[int], k: int) -> int:
3 # Remove duplicates from the input array
4 unique_numbers = set(nums)
5
6 # Initialize the result counter
7 excellent_pairs_count = 0
8
9 # Count the frequency of each bit count value
10 # Key: number of set bits, Value: count of numbers with that many set bits
11 bit_count_frequency = Counter()
12 for number in unique_numbers:
13 bit_count_frequency[number.bit_count()] += 1
14
15 # For each unique number, count how many numbers it can pair with
16 for number in unique_numbers:
17 current_bit_count = number.bit_count()
18
19 # Check all possible bit counts and add valid pairs
20 for other_bit_count, frequency in bit_count_frequency.items():
21 # A pair is excellent if sum of bit counts >= k
22 if current_bit_count + other_bit_count >= k:
23 excellent_pairs_count += frequency
24
25 return excellent_pairs_count
26
1class Solution {
2 public long countExcellentPairs(int[] nums, int k) {
3 // Use HashSet to remove duplicates from the input array
4 Set<Integer> uniqueNumbers = new HashSet<>();
5 for (int number : nums) {
6 uniqueNumbers.add(number);
7 }
8
9 // Initialize result counter
10 long excellentPairsCount = 0;
11
12 // Array to store frequency of numbers with specific bit counts
13 // Index represents the number of set bits (0 to 31 for 32-bit integers)
14 int[] bitCountFrequency = new int[32];
15
16 // Count frequency of each bit count among unique numbers
17 for (int number : uniqueNumbers) {
18 int setBitsCount = Integer.bitCount(number);
19 bitCountFrequency[setBitsCount]++;
20 }
21
22 // For each unique number, find all valid pairs
23 for (int number : uniqueNumbers) {
24 int currentBitCount = Integer.bitCount(number);
25
26 // Check all possible bit counts to find valid pairs
27 for (int otherBitCount = 0; otherBitCount < 32; otherBitCount++) {
28 // If sum of bit counts meets the threshold k, add to result
29 // Since bitCount(a OR b) + bitCount(a AND b) = bitCount(a) + bitCount(b)
30 // We need bitCount(a) + bitCount(b) >= k for an excellent pair
31 if (currentBitCount + otherBitCount >= k) {
32 excellentPairsCount += bitCountFrequency[otherBitCount];
33 }
34 }
35 }
36
37 return excellentPairsCount;
38 }
39}
40
1class Solution {
2public:
3 long long countExcellentPairs(vector<int>& nums, int k) {
4 // Remove duplicates by converting to set
5 unordered_set<int> uniqueNums(nums.begin(), nums.end());
6
7 // Array to count frequency of numbers with specific bit counts
8 // cnt[i] = count of numbers having exactly i set bits
9 vector<int> bitCountFrequency(32);
10
11 // Count the frequency of each bit count in unique numbers
12 for (int num : uniqueNums) {
13 int setBits = __builtin_popcount(num);
14 bitCountFrequency[setBits]++;
15 }
16
17 // Calculate the total count of excellent pairs
18 long long excellentPairsCount = 0;
19
20 // For each unique number, find all valid pairs
21 for (int num : uniqueNums) {
22 int currentBitCount = __builtin_popcount(num);
23
24 // Check all possible bit counts for the second number in the pair
25 for (int i = 0; i < 32; ++i) {
26 // If the sum of bit counts meets the threshold k
27 // (num1 OR num2) + (num1 AND num2) = bitCount(num1) + bitCount(num2)
28 if (currentBitCount + i >= k) {
29 excellentPairsCount += bitCountFrequency[i];
30 }
31 }
32 }
33
34 return excellentPairsCount;
35 }
36};
37
1function countExcellentPairs(nums: number[], k: number): number {
2 // Remove duplicates by converting to set
3 const uniqueNums = new Set<number>(nums);
4
5 // Array to count frequency of numbers with specific bit counts
6 // bitCountFrequency[i] = count of numbers having exactly i set bits
7 const bitCountFrequency: number[] = new Array(32).fill(0);
8
9 // Helper function to count set bits in a number
10 const countSetBits = (num: number): number => {
11 let count = 0;
12 while (num > 0) {
13 count += num & 1;
14 num >>>= 1;
15 }
16 return count;
17 };
18
19 // Count the frequency of each bit count in unique numbers
20 for (const num of uniqueNums) {
21 const setBits = countSetBits(num);
22 bitCountFrequency[setBits]++;
23 }
24
25 // Calculate the total count of excellent pairs
26 let excellentPairsCount = 0;
27
28 // For each unique number, find all valid pairs
29 for (const num of uniqueNums) {
30 const currentBitCount = countSetBits(num);
31
32 // Check all possible bit counts for the second number in the pair
33 for (let i = 0; i < 32; i++) {
34 // If the sum of bit counts meets the threshold k
35 // Key insight: bitCount(num1 OR num2) + bitCount(num1 AND num2) = bitCount(num1) + bitCount(num2)
36 // Therefore, we just need to check if bitCount(num1) + bitCount(num2) >= k
37 if (currentBitCount + i >= k) {
38 excellentPairsCount += bitCountFrequency[i];
39 }
40 }
41 }
42
43 return excellentPairsCount;
44}
45
Time and Space Complexity
Time Complexity: O(n + m²)
where n
is the length of the input array nums
and m
is the maximum number of bits (at most 30 for typical integer constraints).
- Converting
nums
to a set takesO(n)
time - First loop iterates through the set (at most
n
elements) and counts bits for each element:O(n)
time - Second nested loop:
- Outer loop iterates through the set:
O(n)
iterations - Inner loop iterates through the counter's keys (at most
m
different bit counts):O(m)
iterations - Total for nested loops:
O(n × m)
- Outer loop iterates through the set:
- Since
m
is bounded by the number of bits in an integer (typically 30), and in practice the counter will have at mostm
keys, the overall complexity isO(n × m)
which simplifies toO(n)
whenm
is treated as a constant
Space Complexity: O(n)
- The set
s
stores unique elements fromnums
:O(n)
space in worst case - The counter
cnt
stores at mostm
different bit counts (wherem ≤ 30
):O(m)
space, which isO(1)
when treating the bit width as constant - Other variables (
ans
,t
,i
,x
) useO(1)
space - Total space complexity:
O(n) + O(1) = O(n)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Counting Duplicate Numbers Multiple Times
Issue: A common mistake is not removing duplicates from the original array before processing. If you keep duplicates, you might incorrectly count pairs multiple times. For instance, if the array is [1, 1, 1]
and you don't remove duplicates, you might count the pair (1, 1)
three times instead of once.
Why it happens: The problem states that we can form pairs from elements in the array, which might lead to thinking we need to consider each occurrence separately. However, since bitwise operations on the same values always produce the same result, duplicate values don't create new distinct pairs.
Solution: Always convert the input array to a set first to eliminate duplicates:
unique_numbers = set(nums) # Critical step - remove duplicates
Pitfall 2: Misunderstanding the Bit Count Property
Issue: Attempting to calculate (num1 OR num2).bit_count() + (num1 AND num2).bit_count()
directly for each pair, leading to O(n²) complexity and potentially timeout on large inputs.
Why it happens: The problem description naturally suggests checking each pair individually, which seems like the straightforward approach.
Solution: Recognize and use the mathematical property that:
(A OR B).bit_count() + (A AND B).bit_count() == A.bit_count() + B.bit_count()
This allows you to precompute bit counts and group numbers by their bit count, reducing the problem to checking sums of bit counts.
Pitfall 3: Incorrect Pair Counting Logic
Issue: Only counting each valid combination once instead of considering both orderings. For example, counting (3, 5)
but forgetting that (5, 3)
is also a distinct pair according to the problem.
Why it happens: Traditional combination problems often treat (a, b)
and (b, a)
as the same, but this problem explicitly states they are different.
Solution: The correct approach iterates through each number as the first element and counts all valid second elements:
for number in unique_numbers: current_bit_count = number.bit_count() for other_bit_count, frequency in bit_count_frequency.items(): if current_bit_count + other_bit_count >= k: excellent_pairs_count += frequency
This naturally handles both orderings since each number gets a turn being the first element in a pair.
Pitfall 4: Edge Case with Self-Pairs
Issue: Incorrectly handling or forgetting about pairs where both elements are the same number, like (5, 5)
.
Why it happens: It's easy to focus on pairs of different numbers and forget that a number can pair with itself.
Solution: The frequency-based counting approach automatically handles this correctly. When a number pairs with numbers having the same bit count (including itself), the frequency counter includes that number, so self-pairs are naturally counted.
How does quick sort divide the problem into subproblems?
Recommended Readings
Binary Search Speedrun For each of the Speedrun questions you will be given a binary search related problem and a corresponding multiple choice question The multiple choice questions are related to the techniques and template s introduced in the binary search section It's recommended that you have gone through at
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Don’t Miss This!