来自北大算法课的Leetcode题解:1803. 统计异或值在范围内的数对有多少

代码仓库Github | Leetcode solutions @doubleZ0108 from Peking University.

  • 解法1(超时 41/63):朴素的想法肯定是双重循环暴力求解,但看看问题规模肯定会超时

  • 解法2(py3超时 41/63 |JS可以通过更多59/63):

    同样是双重循环暴力求解,但是考虑到数组nums大都很大很大,循环N*(N-1)次是在太容易超时,但low~high这个区间不一定很大,或者说一般都可以接受。

    恰好异或还有一个非常好的性质:x ^ y = t <=> x ^ t = y。

    原来问题可以看做找数组中的两个数x和y,如果他们的异或值t在给定的low~high范围内则总数+1,根据这个性质,可以把问题看作数组中的每个数x,它跟low~high中的每个数t做异或得到y,如果y恰好在原数组中,则证明找到一个数组对x和y。

    还有一件事情要想明白,nums没说每个数彼此不同,某个数可能出现非常多变,但虽然nums[i]相同,但对应的下标i肯定不同,要算做不同的对,因此很有必要统计一下每个数出现了多少遍,这样如果找到了一个匹配的x和y,再乘以x的数量就又可以节省很多计算了。

    说完了这些性质和想法,就要说具体思路了:首先统计nums中每个数出现的频次(可以通过哈希表或是一个countarr),对于nums中的每个数x,计算x和low~high中每个数t的异或值,如果xt在nums中,则证明存在这样的x和y=xt,使得他们的异或值t在规定的范围内,整体sum加和起来即可

    看到C++和Java是可以踩线通过的,但是python3和javascript都还是会超时

    • 改进1(T27% S5%): 用numpy来加速
      • 枚举low~high中的每个数:np.arange(low, high+1) 这个还是能想到的
      • 统计nums中每个数出现的次数:不用哈希表或者Counter(),numpy中有bincount(),可以简单理解为在二进制的语境下统计每个数出现的次数,因此要给顶二进制的最大位数,可以通过bit_length统计所有数字中最大的二进制位数要用多少位
      • 循环也可以通过列表生成式和聚合函数来做,整体代码一场简洁(简介可不一定是好事啊,往往恰恰相反…)
class Solution:
    # 解法1 暴力求解 超时
    def countPairs1(self, nums: List[int], low: int, high: int) -> int:
        res = 0
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                tmp = nums[i] ^ nums[j]
                if tmp >= low and tmp <= high: 
                    res += 1
        return res

    # 解法2
    def countPairs2(self, nums: List[int], low: int, high: int) -> int:
        cnt = Counter(nums)
        res = 0
        for x in cnt.keys():
            for t in range(low, high+1):
                res += cnt[x] * cnt[x^t]
        return res//2

        # # 解法2 的另一种写法,本质是一样的
        # cnt = [0 for _ in range((2*10**4))]
        # for num in nums:
        #     cnt[num] += 1
        # res = 0
        # for x in range(min(nums), max(nums)+1):
        #     if cnt[x] > 0:
        #         for t in range(low, high+1):
        #             res += cnt[x] * cnt[x^t]
        # return res//2

    def countPairs2_(self, nums: List[int], low: int, high: int) -> int:
        # 所有数字中二进制最大需要多少位表示
        maxBits = max(max(x.bit_length() for x in nums), low.bit_length(), high.bit_length())
        # np.bincount() 简单来说是统计nums中每个数出现的次数,当然是在二进制的语境下统计的
        # minlength 最少展开的二进制位数 2^numBits
        cnt = np.bincount(nums, minlength=(1<<maxBits))
        # 合理区间内的每个数,本题无非就是找x^y == 合理区间中的每一个数 的总(x,y)对数
        targets = np.arange(low, high+1)
        # 利用异或的性质 x^y = xor => x^xor = y,因为freq包含了y出现的频次,如果y在某位没出现,则x^y也不可能等于targets中的那个数,也就是说没有数对的异或等于这个数,完全统计low~high中的每个数也就求得到总数
        return int(sum([cnt[targets^x].sum() for x in nums]) // 2)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

doubleZ0108

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值