1711 大餐计数(双指针、字典)

43 篇文章 0 订阅
该篇博客主要介绍了如何解决一道关于找出数组中两数之和为2的幂的组合数问题。作者提供了两种解决方案,一种是使用哈希表统计每个数值出现的次数,然后遍历哈希表计算符合条件的组合数;另一种是采用双指针方法,先对数组去重并排序,再通过双指针查找满足条件的数对。两种方法都考虑到了组合数重复计算的情况并进行了修正。
摘要由CSDN通过智能技术生成

1. 问题描述:

大餐是指恰好包含两道不同餐品的一餐,其美味程度之和等于 2 的幂。你可以搭配任意两道餐品做一顿大餐。给你一个整数数组 deliciousness ,其中 deliciousness[i] 是第 i 道餐品的美味程度,返回你可以用数组中的餐品做出的不同大餐的数量。结果需要对 10 ^ 9 + 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 <= 10^5
  • 0 <= deliciousness[i] <= 2^20

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/count-good-meals

2. 思路分析:

① 分析题目可以知道我们需要在deliciousness数组中找到所有两个数字相加和为2的n次幂的组合数目,所以这道题目本质上求解的是两个数字相加的和为某个target的的数目,只是这里求解的target存在多个,由题目可以知道deliciousness[i]的最大值是2 ^ 20,所以两个数字之和最大是2 ^ 21,需要尝试求解的target为2的0次幂到2的21次幂,一开始想到的是哈希表来进行计数(使用哈希表来寻找和为当前的2的i次幂的另外一个数字是否存在),因为使用的是python语言所以使用字典来进行计数,遍历字典中的键值对,对于当前的键,寻找和为2 ^ 0~2 ^ 21所有2的i次幂的另外一个数字是否存在,如果存在说明两个数字的和相加为当前的2 ^ i次幂,并且这里需要注意的是需要分两种情况进行判断:第一种情况是当另外一个数字等于当前遍历的键的时候并且两者相加的和为2 ^ i次幂这个时候累加的结果为(dic[key] * dic[key] - 1) // 2,具体的例子:当前的键为1,寻找的另外一个数字也为1,这个时候两个数字相加等于2的1次幂,当前的键为2另外一个数字也为2,这个时候两数相加等于2 ^ 2,这个时候的组合数目应该等于等差数列的求和,也就是(dic[key] * dic[key] - 1) // 2,第二种情况是两个相加的数字不相等的时候那么组合的数目等于两者在字典中对应的数目相乘,因为是遍历字典中所有的值所以当两个相加的数字不相等的时候重复计算了一次组合的数目,所以需要将两个加数不等的组合数目的结果除以2

② 除了①中使用哈希表来寻找两个数字相加的和为target的思路之外,还可以使用双指针的方法进行解决,这个与之前的两数之和等于target的本质上是一样的,通过对deliciousness数组的元素进行计数并且对其进行去重之后排序然后使用双指针的方法,具体的实现其实也不复杂我们可以依次计算deliciousness数组中是否存在两数之和等于2 ^ 0~2 ^ 21次这个范围的数字,通过双指针来计算对应的组合数目即可

3. 代码如下:

字典:

import collections
from typing import List


class Solution:
    def countPairs(self, deliciousness: List[int]) -> int:
        # 通过哈希表寻找和为target的另外一个数字是否存在
        dic = collections.defaultdict(int)
        for n in deliciousness:
            dic[n] += 1
        res, count, mod = 0, 0, 10 ** 9 + 7
        for key, value in dic.items():
            for i in range(22):
                # 两个相加的数字相等并且等于当前的2的i次幂
                if (1 << i) - key == key: 
                    count += value * (value - 1) // 2 % mod
                # 两个数字不等的情况而且等于当前的2的i次幂
                elif (1 << i) - key in dic:
                    res = (res + value * dic[(1 << i) - key]) % mod
        # 两个加数不等的时候重复计算了一次所以需要除以2
        return (res // 2 + count) % mod

双指针:

import collections
from typing import List


class Solution:
    def countPairs(self, deliciousness: List[int]) -> int:
        mod = 10 ** 9 + 7
        dic = collections.defaultdict(int)
        for n in deliciousness:
            dic[n] += 1
        deliciousness = list(set(deliciousness))
        deliciousness.sort()
        i, res = 0, 0
        # 尝试2的零次方~最后一个元素的两倍
        while 2 ** i <= deliciousness[-1] * 2:
            l, r = 0, len(deliciousness) - 1
            while l <= r:
                if deliciousness[l] + deliciousness[r] < 2 ** i:
                    l += 1
                elif deliciousness[l] + deliciousness[r] > 2 ** i:
                    r -= 1
                else:
                    # 当最后两个元素相等的时候而且等于了当前的2的i次幂那么肯定应该需要特殊处理
                    if l == r:
                        res = (res + dic[deliciousness[l]] * (dic[deliciousness[l]] - 1) // 2) % mod
                        break
                    else:
                        res = (res + dic[deliciousness[l]] * dic[deliciousness[r]]) % mod
                        r -= 1
                        l += 1
            i += 1
        return res

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值