对数组的连续子数组进行异或

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) 时间内解决这个问题。具体来说:

对于任何kk XOR k == 0; 对于任何kk 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 + 1n - i都是奇数,即当且仅当i是偶数并且@987654348 @ 很奇怪。因此,对于偶数长度的列表,结果始终为 0,而对于奇数长度的列表,结果是列表中每隔一个元素的 XOR。 +1,并编辑修复了一个小错误;希望你不介意。附言。您真的不需要素数分解来证明奇数的乘积是奇数,一点modular arithmetic 也可以做到这一点。但我真的不应该抱怨,因为我非常喜欢使用FTA 作为一个方便的大锤来和自己一起破解坚果。 :)

以上是关于对数组的连续子数组进行异或的主要内容,如果未能解决你的问题,请参考以下文章

最短无序连续子数组

Leetcode 581.最短无序连续子数组

数组的连续最大子段和

leetcode581 最短无序连续子数组(Easy不简单)

力扣----16. 最短无序连续子数组(JavaScript实现)

如何将 CSV 数据导入多个数组并通过 VBA 中的函数或子函数返回多个数组?