🌈【LeetCode.大餐计数】- JavaScript =>哈希+位运算

418 阅读2分钟

「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战


说明:文章部分内容及图片出自网络,如有侵权请与我本人联系(主页有公众号:小攻城狮学前端)

作者:小只前端攻城狮、 主页:小只前端攻城狮的主页、 来源:掘金

GitHub:P-J27、 CSDN:PJ想做前端攻城狮

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


题目描述

大餐 是指 恰好包含两道不同餐品 的一餐,其美味程度之和等于 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;
    }
};

感谢阅读,希望能对你有所帮助,文章若有错误或者侵权,可以在评论区留言或在我的主页添加公众号联系我。

写作不易,如果觉得不错,可以「点赞」+「评论」 谢谢支持❤