📌  相关文章
📜  来自两个给定数组的所有对的按位与的按位XOR(1)

📅  最后修改于: 2023-12-03 14:55:29.775000             🧑  作者: Mango

题目描述

给定两个长度相同的非负整数数组 nums1 和 nums2,找到所有最大的 (nums1[i] AND nums2[j]) XOR (XOR 表示按位异或)值,其中 0 ≤ i, j < n 分别是 nums1 和 nums2 的下标。

示例

输入:nums1 = [1,2,3], nums2 = [4,5,6,7]

输出:[6,7]

解释:

|nums1|nums2| |---|---| |1|4| |2|5| |3|6| ||7|

所有数对按位与的结果如下:

|nums1[i]&nums2[j]| |---| |0| |0| |2| |3| |0| |1| |2| |3|

其中最大的异或值为 6 和 7,输出这两个数。

解法

这道题看起来很复杂,但其实只需要一位一位地判断即可。

我们可以从最高位开始,尽量让两个数字的这一位都是 1,然后判断其他位的组合情况。

对于两个数的每一位,如果要想让异或结果最大,需要满足下列条件之一:

  • 两个数的这一位都是 0。
  • 两个数的这一位分别是 0 和 1。

因此,我们可以分别统计两个数组的每一位最优的情况,然后将它们放在一起就行了。

代码实现
class Solution:
    def findMaximumXOR(self, nums1: List[int], nums2: List[int]) -> List[int]:
        res = 0
        mask = 0
        for i in range(31, -1, -1):
            mask |= 1 << i
            s = set(num & mask for num in nums1)
            t = set(num & mask for num in nums2)
            tmp = res | (1 << i)
            
            for prefix in s:
                if tmp ^ prefix in t:
                    res = tmp
                    break
        return [res ^ num for num in nums1 + nums2 if num & mask == res]
代码说明

这里引入一个叫做“前缀”的概念。

我们将每一个二进制数的前缀求出来,存入一个集合中。比如:

0010101101101010
└─┬─┘└───┬────┘
  └───┬───┘
   前缀集合 { 0b0, 0b1, 0b10, 0b101, ..., 0b10101101101 }

根据上面提到的策略,我们可以枚举每一位,然后逐步求出最大值。

对于最大值的求法,与动态规划类似,记录每一位能够达到的最大值,然后递推求出结果。

当我们遍历完了所有的位,就得到了最大的异或结果。此时我们还需要将对应的数字求出来。

对于两个数相同的前缀部分,异或结果当然是 0,但这部分前缀并不能作为最大值。因此,我们需要将前缀相同的两个数一起考虑。

拿示例数据来看,最终的前缀是 0110。因此,我们需要找到 nums1 和 nums2 中二进制表示前缀为 0110 的数字,并将它们异或起来。这样才得到了真正的最大异或结果。

同时需要注意的是,前缀的集合是对 nums1 和 nums2 分别求出的。而前缀相同的两个数可能分别来自 nums1 和 nums2,因此我们需要将他们全部加起来,再检查它们是否可行。