「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战」
说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)
作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
题目描述
大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 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) 。
思路分析
从数据量可以推断出复杂度大致应该是n(logn),为了减小时间成本,顺着思路就只有二分查找思路比较符合本题了。情况如下: 1.遍历数组找到比当前数大但是不大于数组最大数两倍小的所有2的n次幂 2.将对应的2的n次幂与当前数做减法,并用二分查找查找数组中该数的左右边界,右边界-左边界+1便得此次的次数,以此类推继续遍历。
解法1:Map
var countPairs = function (deliciousness) {
const mod = 10 ** 9 + 7
const map = new Map()
for (const num of deliciousness) {
if (!map.has(num)) map.set(num, 0)
map.set(num, map.get(num) + 1)
}
let ans = 0
const visited = new Set()
for (const [num, count] of map) {
let flag = false
for (let i = 0; i <= 21; i++) {
const sum = 2 ** i
if (!visited.has(sum - num) && map.has(sum - num)) {
if (sum - num === num) {
ans = (ans + (count * (count - 1)) / 2) % mod
} else {
ans = (ans + count * map.get(sum - num)) % mod
}
flag = true
}
}
if (flag) visited.add(num)
}
return ans
}
解法2:Map+位运算
分析:左移运算符相当于加倍,即x * 2^y,设置 x 为 1,则为题目要求的 2 的幂。
class Solution {
public:
typedef long long LL;
int mod = 1e9 + 7;
int countPairs(vector<int>& deliciousness) {
unordered_map<int, int> cnt;
for(auto & x : deliciousness) ++cnt[x];
int ans = 0;
LL a = 0, b = 0;
for(auto & [k, c] : cnt){
for(int i = 0; i <= 21; i++){
int target = (1 << i) - k;
if(cnt.count(target)){
if(target == k) a += c * (c - 1LL) / 2;
else b += (LL)c * cnt[target];
}
}
}
ans = (a + b / 2) % mod;
return ans;
}
};
感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。
写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤