📅  最后修改于: 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,然后判断其他位的组合情况。
对于两个数的每一位,如果要想让异或结果最大,需要满足下列条件之一:
因此,我们可以分别统计两个数组的每一位最优的情况,然后将它们放在一起就行了。
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,因此我们需要将他们全部加起来,再检查它们是否可行。