一个数组的 异或总和 定义为数组中所有元素按位 XOR 的结果;如果数组为 空 ,则异或总和为 0 。
例如,数组[2,5,6] 的 异或总和 为 2 XOR 5 XOR 6 = 1 。
给你一个数组 nums ,请你求出 nums 中每个 子集 的 异或总和 ,计算并返回这些值相加之 和 。
注意:在本题中,元素 相同 的不同子集应 多次 计数。
数组 a 是数组 b 的一个 子集 的前提条件是:从 b 删除几个(也可能不删除)元素能够得到 a 。
示例1
输入:nums = [1,3]
输出:6
解释:[1,3] 共有 4 个子集:
- 空子集的异或总和是 0 。
- [1] 的异或总和为 1 。
- [3] 的异或总和为 3 。
- [1,3] 的异或总和为 1 XOR 3 = 2 。
0 + 1 + 3 + 2 = 6
示例2
输入:nums = [5,1,6]
输出:28
解释:[5,1,6] 共有 8 个子集:
- 空子集的异或总和是 0 。
- [5] 的异或总和为 5 。
- [1] 的异或总和为 1 。
- [6] 的异或总和为 6 。
- [5,1] 的异或总和为 5 XOR 1 = 4 。
- [5,6] 的异或总和为 5 XOR 6 = 3 。
- [1,6] 的异或总和为 1 XOR 6 = 7 。
- [5,1,6] 的异或总和为 5 XOR 1 XOR 6 = 2 。
0 + 5 + 1 + 6 + 4 + 3 + 7 + 2 = 28
示例3
输入:nums = [3,4,5,6,7,8]
输出:480
解释:每个子集的全部异或总和值之和为 480 。
-
解法1:
from typing import List
def subsetXORSum(nums: List[int]) -> int:
length = len(nums)
res = 0
for index in range(0, 2 ** length):
subset_res = 0
for sub_index in range(0, length):
cur_bit = (index >> sub_index) & 1
if cur_bit == 1:
subset_res ^= nums[sub_index]
res += subset_res
return res
#nums = [1, 3]
#nums = [5, 1, 6]
nums = [3, 4, 5, 6, 7, 8]
print(subsetXORSum(nums))
## 测试脚本,用于显示上述脚本的执行流程过程。
from typing import List
def subsetXORSum(nums: List[int]) -> int:
length = len(nums)
res = 0
for index in range(0, 2 ** length):
subset_res = 0
test = ''
test2 = ''
test3 = ''
for sub_index in range(0, length):
cur_bit = (index >> sub_index) & 1
test = test + str(cur_bit)
test3 = str(cur_bit) + test3
if cur_bit == 1:
subset_res ^= nums[sub_index]
test2 = test2 + str(nums[sub_index])
else:
test2 = test2 + ' '
res += subset_res
print(test3, '\t本次循环', index, '的二进制表示')
print(test, '\t本次循环', index, '的二进制反序表示')
print(test2, '\t本次循环子集组合')
print(subset_res, '\t该子集异或结果')
print(res, '\t目前为止所有子集的总和')
print()
return res
#nums = [1, 3]
#nums = [5, 1, 6]
nums = [3, 4, 5, 6, 7, 8]
print(subsetXORSum(nums))
这段代码实现了一个函数 subsetXORSum
,该函数接受一个整数列表 nums
作为输入,并返回该列表中所有子集的异或和。
代码解析如下:
导入了
typing
模块中的List
类型,用于标注函数参数和返回值的类型。定义了函数
subsetXORSum
,参数为nums
,返回值为整数类型。创建了变量
length
并赋值为输入列表nums
的长度。创建了变量
res
并初始化为0,用于存储子集异或和的累加结果。使用循环遍历所有可能的子集,循环条件为
range(0, 2 ** length)
。其中2 ** length
表示子集个数的上限,因为每个元素都有两种选择:包含或不包含。在内部循环中,使用
sub_index
遍历子集的位索引,循环条件为range(0, length)
。该循环用来判断当前子集中是否包含 列表nums
第sub_index
位的元素。使用位运算
(index >> sub_index) & 1
取出当前子集的第sub_index
位,并将结果赋给变量cur_bit
。如果cur_bit
等于1,则表示该子集包含 列表nums
第sub_index
位元素。如果
cur_bit
等于1,则将该子集中的元素与subset_res
进行异或操作,并将结果赋给subset_res
。这样就得到了当前子集的异或和。将每个子集的异或和累加到变量
res
上。循环结束后,返回最终的累加结果
res
。调用函数
subsetXORSum
,传入列表nums
,并将结果打印输出。你可以根据需要修改nums
的值,取消注释对应的行即可。例如,如果nums = [1, 3]
,则输出为6;如果nums = [5, 1, 6]
,则输出为28;如果nums = [3, 4, 5, 6, 7, 8]
,则输出为480。
-
解法2:
该解法的解释可以参考下篇。python- 算法题-8_找出所有子集的异或总和再求和_扩展
def subsetXORSum(nums: list[int]) -> int:
res = 0
length = len(nums)
for num in nums:
res |= num
return res << (length - 1)
#nums = [1, 3]
#nums = [5, 1, 6]
nums = [3, 4, 5, 6, 7, 8]
print(subsetXORSum(nums))
num * 2 ** N = num << N
,num 和 N 必须是整数。
这段 Python 3 代码实现了一个函数 subsetXORSum
,它接受一个整数列表 nums
作为参数,并返回对所有子集进行异或操作的结果。
让我们逐行解析这段代码:
def subsetXORSum(nums: list[int]) -> int:
这一行定义了一个名为 subsetXORSum
的函数,它接受一个名为 nums
的参数,该参数是一个整数列表,并且返回一个整数。
res = 0
在函数内部,我们初始化一个整数变量 res
并将其设置为 0。该变量用于存储异或操作的结果。
length = len(nums)
我们使用 len()
函数获取 nums
列表的长度,并将其赋值给变量 length
,以便在后面的计算中使用。
for num in nums:
res |= num
这是一个循环语句,用于遍历 nums
列表中的每个元素。在循环中,我们使用位运算符 |=
将当前元素 num
与 res
进行按位或(bitwise OR)操作,并将结果存回 res
。这样做的目的是将所有的元素进行异或操作。
return res << (length - 1)
最后,我们使用位运算符 <<
将 res
左移 (length - 1)
位,然后将结果返回。这是因为对于 nums
列表中的每个元素,在所有子集中都有 (length - 1)
个子集中包含了该元素。所以我们将结果左移 (length - 1)
位,相当于将每个元素的异或结果乘以 2 的 (length - 1)
次方。
nums = [3, 4, 5, 6, 7, 8]
print(subsetXORSum(nums))
在这段代码的最后,我们定义了一个名为 nums
的列表,并将其赋值为 [3, 4, 5, 6, 7, 8]
。然后我们调用 subsetXORSum
函数,将 nums
作为参数传递给它,并打印函数的返回值。
扩展:
XOR 全称为异或运算,是一种逻辑运算符,通常用符号 “^” 表示。其作用是对两个二进制数进行逐位比较,如果相同则输出 0,不同则输出 1。
XOR 的真值表如下:
A | B | A ^ B |
---|---|---|
0 | 0 | 0 |
0 | 1 | 1 |
1 | 0 | 1 |
1 | 1 | 0 |
其中 A 和 B 均为二进制数值(可以是 0 或 1)。
XOR 运算有一些特殊的性质:
- 相同数值 XOR 后结果为 0。即 a ^ a = 0。
- 任何数值与 0 进行 XOR 运算,结果均为其本身。即 a ^ 0 = a。
- XOR 运算支持交换律和结合律,即 a ^ b = b ^ a,a ^ (b ^ c) = (a ^ b) ^ c。
- XOR 运算可以用于找到只出现一次的数字,例如在一个数组中,除了某个元素只出现一次以外,其余每个元素均出现两次。使用 XOR 运算,可以将所有数字进行逐个异或,最终结果即为只出现一次的数字。
在计算机领域,XOR 运算还常常被用来进行二进制数据的加密和解密。
扩展1:
num * 2 ** N = num << N
这个等式表示,将一个数字 num
左移 N
位,结果与将 num
乘以 2
的 N
次方相等。
数学公式为:
其中,<<
表示左移运算符,将一个数的二进制表示向左移动指定的位数。
请注意,上述等式成立的前提是 num
和 N
必须是整数。
参考:
https://www.bilibili.com/video/BV1YT4y117AH?p=9&vd_source=fd551c73306d10ac7c5d19c2369e1ce3
https://leetcode.cn/problems/sum-of-asub_l-subset-xor-totals/
https://leetcode.cn/problems/sum-of-asub_l-subset-xor-totals/solution/zhi-xu-yao-li-jie-zi-ji-he-er-jin-zhi-sh-h9l2/
https://leetcode.cn/problems/sum-of-all-subset-xor-totals/solution/sum-of-all-subset-xor-totals-by-leetcode-o5aa/
https://leetcode.cn/problems/sum-of-all-subset-xor-totals/solution/onsuan-fa-jian-ming-jiang-jie-by-yuyinsl-9sod/