Count Good Meals
return count of set of meals whose deliciousness score (sum of both meals) is power of 2
Count Good Meals
Problem
A good meal has exactly two different food items, and the sum of their deliciousness is a power of two.
You are given an array deliciousness. The value deliciousness[i] is the deliciousness of the ith food item.
Return the number of different good meals you can make, modulo 10^9 + 7.
Items with different indexes are different, even if their values are the same.
Examples
Example 1
Input: deliciousness = [1,3,5,7,9]
Output: 4
(1,3)has sum4.(1,7)has sum8.(3,5)has sum8.(7,9)has sum16.- All sums are powers of two, so the answer is 4.
Example 2
Input: deliciousness = [1,1,1,3,3,3,7]
Output: 15
(1,1)can be made in 3 ways. The sum is2.(1,3)can be made in 9 ways. The sum is4.(1,7)can be made in 3 ways. The sum is8.- Total =
3 + 9 + 3 = 15.
Constraints
1 <= deliciousness.length <= 10^50 <= deliciousness[i] <= 2^20
Main Idea
This is a Two Sum problem with a twist. Instead of one target, we have many targets: 1, 2, 4, 8, 16, ....
For each number num, and for each power of two power, we ask:
"Have I already seen the number power - num?"
- We keep a hash map that stores each value and how many times we saw it.
- If
complement = power - numis in the map, we can make that many good meals withnum. - We add the count to the answer.
- After we check all powers, we add
numto the map.
Why we count only earlier items
- We check the map before adding the current number.
- So each pair is counted once, when its second item arrives.
- Also, a number is never paired with itself.
How many powers do we check?
- The biggest sum is
max + max = 2 * max. - So we check powers of two from
1up to2 * max. - Since
max <= 2^20, there are at most 22 powers (2^0to2^21). - That is a small fixed number.
Solution 1: Brute Force
We check every pair of items. A sum is a power of two if sum > 0 and sum & (sum - 1) === 0.
var countPairs = function (deliciousness) {
let output = 0;
for (let i = 0; i < deliciousness.length; i++) {
for (let j = i + 1; j < deliciousness.length; j++) {
let sum = deliciousness[i] + deliciousness[j];
if (sum > 0 && (sum & (sum - 1)) === 0) {
output++;
}
}
}
return output;
};
How the Power of Two Check Works
- A power of two has only one bit set in binary. For example,
8is1000. sum - 1flips that bit and sets all the bits after it. For example,7is0111.- So
sum & (sum - 1)is0only for a power of two. - We need
sum > 0, because0 & -1is also0, but0is not a power of two.
Complexity
- Time Complexity:
O(n²)nis the number of food items.- We check every pair. With
n = 10^5, this is about5 * 10^9pairs, which is too slow.
- Space Complexity:
O(1)- We use only a few variables.
Note: This version does not use
% MOD. For large inputs the count can pass10^9 + 7, so it would give a wrong answer. It is only good for small inputs.
Solution 2: Hash Map (Optimal)
var countPairs = function (deliciousness) {
let map = new Map();
let output = 0;
let MOD = 1e9 + 7;
let max = Math.max(...deliciousness);
for (let num of deliciousness) {
// try every power of two that can be a sum
for (let power = 1; power <= max * 2; power *= 2) {
let complement = power - num;
// the partner was seen before
if (map.has(complement)) {
output = (output + map.get(complement)) % MOD;
}
}
// save the current number
map.set(num, (map.get(num) || 0) + 1);
}
return output;
};
How the Code Works
Step 1: Set up
mapstores each value and how many times we saw it.outputis the answer.MODis10^9 + 7.maxis the biggest value. The biggest possible sum ismax * 2.
Step 2: Read each number
- For each
num, we trypower = 1, 2, 4, 8, ...up tomax * 2. power *= 2moves to the next power of two.
Step 3: Find the partner
complement = power - numis the value we need to reach this power.- If it is negative, it is never in the map, so nothing happens.
- If it is in the map,
map.get(complement)tells us how many earlier items have this value. - Each of them makes a different good meal with the current item, so we add that count.
- We use
% MODto keep the number small.
Step 4: Save the number
- We add
numto the map after the loop over powers. - This way, the current item is not paired with itself.
Step 5: Return the answer
outputholds the total number of good meals.
Dry Run
Let us take deliciousness = [1, 3, 5, 7, 9].
Here max = 9, so we check powers 1, 2, 4, 8, 16.
num | Matching powers | Partner found in the map | Added | output | Map after the step |
|---|---|---|---|---|---|
1 | none | none | 0 | 0 | {1: 1} |
3 | 4 (needs 1) | 1 | 1 | 1 | {1: 1, 3: 1} |
5 | 8 (needs 3) | 3 | 1 | 2 | {1: 1, 3: 1, 5: 1} |
7 | 8 (needs 1) | 1 | 1 | 3 | {1: 1, 3: 1, 5: 1, 7: 1} |
9 | 16 (needs 7) | 7 | 1 | 4 | {1: 1, 3: 1, 5: 1, 7: 1, 9: 1} |
The answer is 4.
Now let us look at repeated values. Take [1, 1, 1].
- First
1: the map is empty, so we add nothing. Map is{1: 1}. - Second
1: the power2needs1, and the map has one1. We add1. Map is{1: 2}. - Third
1: the power2needs1, and the map has two1s. We add2. Total =3.
This gives 3 pairs, which is correct, because three items make 3 pairs.
Complexity
- Time Complexity:
O(n * log(max))nis the number of items andmaxis the biggest value.- For each item, we check about
log2(2 * max)powers. This is at most 22. - Each map operation takes
O(1)on average. - So it is close to
O(n), because 22 is a fixed small number.
- Space Complexity:
O(n)- The map holds one entry for each different value, which is at most
n.
- The map holds one entry for each different value, which is at most
Tip:
Math.max(...deliciousness)works for10^5items, but it can fail with much bigger arrays. A simple loop to find the max is safer.
Comparison
| Approach | Time Complexity | Space Complexity | Works for n = 10^5? |
|---|---|---|---|
| Brute Force | O(n²) | O(1) | No (too slow, no modulo) |
| Hash Map | O(n * log(max)) | O(n) | Yes |
Summary
- A good meal is a pair of items whose sum is a power of two.
- This is like Two Sum, but with many targets:
1, 2, 4, 8, .... - There are at most about 22 powers, because the sum is at most
2^21. - For each item, we check each power and look for
power - numin the map. - We save the current item after checking, so every pair is counted once.
- The map stores counts, so repeated values give the correct number of pairs.
- We use
% MODon the answer. - Brute force takes
O(n²)time and is too slow. The hash map solution runs inO(n * log(max))time and usesO(n)space.