题目
大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 2 的幂。
你可以搭配 任意 两道餐品做一顿大餐。
给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i 道餐品的美味程度,返回你可以用数组中的餐品做出的不同 大餐 的数量。结果需要对 109 + 7 取余。
注意,只要餐品下标不同,就可以认为是不同的餐品,即便它们的美味程度相同。
示例 1:
输入:deliciousness = [1,3,5,7,9]
输出:4
解释:大餐的美味程度组合为 (1,3) 、(1,7) 、(3,5) 和 (7,9) 。
它们各自的美味程度之和分别为 4 、8 、8 和 16 ,都是 2 的幂。
示例 2:
输入:deliciousness = [1,1,1,3,3,3,7]
输出:15
解释:大餐的美味程度组合为 3 种 (1,1) ,9 种 (1,3) ,和 3 种 (1,7) 。
提示:
1 <= deliciousness.length <= 105
0 <= deliciousness[i] <= 220
解题思路与算法
一. 暴力解法
显然,此题可以通过暴力解法解决:双层for循环找到所有满足要求的两数之和,并统计次数。时间复杂度为O(n^2), 超时。
二. 哈希表记录数字出现次数
通过暴力解法我们可以发现,在查找两数之和的第二个数时,用了O(n)时间(遍历整个数组)。又由于我们只关心次数,而并非具体的哪两个数,所以我们可以用哈希表记录数字与其出现次数,这样可以把查找时间优化到O(1)。注意到此题的输入限制,数字最大为2^20,所以我们可以在有限的空间枚举。代码如下:
public int countPairs(int[] deliciousness) {
Map<Integer, Integer> map = new HashMap<>();
int mod = 1000000007;
int answer = 0;
int length = deliciousness.length;
for (int num : deliciousness) {
int powerOfTwo = 1;
// 为什么是21? 因为数字最大为2^20, 2^20 + 2^20 = 2^21为可能的最大值,不可能再大啦!
for (int i = 0; i <= 21; i++) {
if (powerOfTwo >= num && map.containsKey(powerOfTwo - num)) {
answer += map.get(powerOfTwo - num);
answer %= mod;
}
powerOfTwo *= 2;
}
map.put(num, map.getOrDefault(num, 0) + 1);
}
return (int)answer;
}
参考:https://leetcode-cn.com/problems/count-good-meals/solution/onshi-jian-fu-za-du-jie-fa-liang-shu-zhi-v0fo/