对数组的连续子数组进行异或
Posted
技术标签:
【中文标题】对数组的连续子数组进行异或【英文标题】:XOR on contiguous subarrays of an array 【发布时间】:2017-05-26 00:00:33 【问题描述】:从一个数组中,我需要找到通过对连续子数组进行异或运算获得的值,然后对由此获得的值进行异或运算。
输入
一行包含作为数组元素的整数。
例如[1,2,3]
输出
将每个测试用例对应的答案打印在单独的行中。
到目前为止,我设法使用循环和递归方法构建了两种策略。 我的方法都没有在大输入大小上提供良好的性能。
例如1 异或 2 异或 3 异或 (1 异或 2) 异或 (2 异或 3) 异或 (1 异或 2 异或 3) = 2
你能建立一个更好的算法吗?也许是动态编程方法?
from functools import reduce
# Calculate the XOR
def XOR(L):
return reduce(lambda x, y: x ^ y, L)
# Recursive approach
def allSubArraysXOR(L,L2=None):
if L2==None:
L2 = L[:-1]
if L==[]:
if L2==[]:
return 0
return allSubArraysXOR(L2,L2[:-1])
return XOR(L) ^ allSubArraysXOR(L[1:],L2)
# Loop - yielding approach
def getAllWindows(L):
for w in range(1, len(L)+1):
for i in range(len(L)-w+1):
yield XOR(L[i:i+w])
a = [int(a_temp) for a_temp in input().strip().split(' ')]
print(allSubArraysXOR(a))
# print(XOR(getAllWindows(a)))
【问题讨论】:
如果数组中的值可能是任意的,请更改示例,例如[ 13, 42, 4711 ]
.
【参考方案1】:
我们不需要枚举 (2**n) 个子数组来解决这个问题。
XOR 有一些有用的属性,我们可以利用这些属性在 O(n) 时间内解决这个问题。具体来说:
对于任何k
:k XOR k == 0
;
对于任何k
:k XOR 0 == k
。
XOR is both commutative and associative。
要解决您的问题,我们首先需要计算每个元素在子数组中出现的次数。任何出现偶数次的元素都可以忽略。其余的需要一起异或(每个只取一次)。
让我们看看这如何应用于您的示例:
1 XOR 2 XOR 3 XOR (1 XOR 2) XOR (2 XOR 3) XOR (1 XOR 2 XOR 3) = # open brackets
1 XOR 2 XOR 3 XOR 1 XOR 2 XOR 2 XOR 3 XOR 1 XOR 2 XOR 3 = # reorder
1 XOR 1 XOR 1 XOR 2 XOR 2 XOR 2 XOR 2 XOR 3 XOR 3 XOR 3 = # group
(1 XOR 1 XOR 1) XOR (2 XOR 2 XOR 2 XOR 2) XOR (3 XOR 3 XOR 3) = # remove pairs
1 XOR 0 XOR 3 =
1 XOR 3 =
2
以下是这个想法的 O(n) 实现:
def xor_em(lst):
n = len(lst)
ret = 0
for i, el in enumerate(lst):
count = (i + 1) * (n - i)
if count % 2:
ret ^= el
return ret
print xor_em([1, 2, 3])
子数组的计数由
count = (i + 1) * (n - i)
观察到当前元素左侧有i + 1
元素(包括它自己)和右侧有n - i
(也包括它自己)。将两者相乘得出从当前元素左侧开始到右侧结束的子数组的数量。
我们现在已将问题简化为查找乘积为奇数的对 (i + 1)
和 (n - i)
。请注意,获得奇数乘积的唯一方法是将两个本身为奇数的数字相乘(这可以通过考虑两个被乘数的素数分解来看出)。
有两种情况需要考虑:
当n
是偶数时,(i + 1)
和(n - i)
之一总是偶数。这意味着对于偶数长度的列表,该算法始终返回零。
当n
是奇数时,(i + 1) * (n - i)
对于i = 0, 2, 4, ..., (n - 1)
是奇数。
这导致以下简化的解决方案:
def xor_em(lst):
if len(lst) % 2 == 0:
return 0
else:
return reduce(operator.xor, lst[::2])
【讨论】:
谢谢@NPE,你真是太好了——你是怎么知道异或的这个属性的? @Michael:他们是众所周知的。例如,参见cs.umd.edu/class/sum2003/cmsc311/Notes/BitOp/xor.html。将 XOR 视为“位翻转”运算符的一种方式是:如果您将相同的位翻转两次,您就会回到开始的位置。这意味着我们可以认为 XOR 在某种意义上是“无损的”(不像 AND 和 OR,它们会丢失位)。 @Michael:我很高兴。另请参阅math.stackexchange.com/questions/293793/… 你可以进一步简化你的解决方案,注意(i + 1) * (n - i)
是奇数当且仅当i + 1
和n - i
都是奇数,即当且仅当i
是偶数并且@987654348 @ 很奇怪。因此,对于偶数长度的列表,结果始终为 0,而对于奇数长度的列表,结果是列表中每隔一个元素的 XOR。
+1,并编辑修复了一个小错误;希望你不介意。附言。您真的不需要素数分解来证明奇数的乘积是奇数,一点modular arithmetic 也可以做到这一点。但我真的不应该抱怨,因为我非常喜欢使用FTA 作为一个方便的大锤来和自己一起破解坚果。 :)以上是关于对数组的连续子数组进行异或的主要内容,如果未能解决你的问题,请参考以下文章
leetcode581 最短无序连续子数组(Easy不简单)