📌  相关文章
📜  二进制数组中的最小翻转,使得大小为K的连续子数组的XOR具有不同的奇偶校验(1)

📅  最后修改于: 2023-12-03 15:06:20.600000             🧑  作者: Mango

二进制数组中的最小翻转

问题描述

给定一个长度为N的二进制数组A和一个整数K,每次可以对数组A中的一个连续子数组翻转,即将0变成1,1变成0。请确定最少需要多少次翻转才能使得大小为K的连续子数组的XOR具有不同的奇偶校验。

思路

我们可以考虑将数组A分成若干个大小为K的连续子数组,对于每个子数组,我们计算它的XOR值的奇偶性,并将所有子数组按照奇偶性分成两个集合。对于其中任意一个集合,如果它的大小超过了N/2,我们可以选择将其中每个子数组取反得到另外一个集合,使得两个集合的大小变得接近。因此,我们只需要考虑两个集合大小都不超过N/2的情况。

假设某个集合中有M个子数组,我们可以将它们按照XOR值的奇偶性分成两个集合A和B。A中的子数组的XOR值都是偶数,B中的子数组的XOR值都是奇数。我们可以将集合A中的所有子数组翻转,然后将它们与集合B中的所有子数组拼接起来得到一个新的长度为2M的二进制数组。由于翻转一个子数组只会改变它的XOR值的奇偶性,因此我们可以证明,这个新的二进制数组中,大小为K的任意连续子数组的XOR值都有不同的奇偶性。

因此,我们可以将原问题转化为对于长度为K的所有连续子数组,求出它们的XOR值的奇偶性是偶数的数量和奇数的数量的差的绝对值,然后取所有差的绝对值的最小值即可。这个问题可以通过对长度为K的连续子数组中的所有元素计算XOR值,并用一个计数器维护奇偶性来实现。

时间复杂度

计算XOR值和维护计数器的时空复杂度都是O(K),因此总的时间复杂度是O(NK),空间复杂度是O(1)。

代码
def min_flips(A: List[int], K: int) -> int:
    # 统计所有奇偶性组合的数量,并记录每种组合中XOR值的奇偶性是偶数的数量
    count = [0] * (1 << K)
    even = [0] * (1 << K)
    for i in range(len(A) - K + 1):
        val = 0
        for j in range(K):
            val ^= A[i+j]
        count[val] += 1
        if i % 2 == 0:
            even[val] += 1
    
    # 计算最小翻转数量
    ans = len(A)
    for i in range(1 << K):
        if count[i] == 0:
            continue
        if count[i] > len(A) // 2:
            count[i] = len(A) - count[i]
            even[i] = count[i] - even[i]
        if len(A) % 2 == 0:
            ans = min(ans, even[i])
        else:
            ans = min(ans, min(even[i], count[i] - even[i]))
    return ans
测试
assert min_flips([1, 0, 0, 0, 1, 0, 0, 1], 3) == 1
assert min_flips([0, 0, 0, 1, 0, 1, 1, 0], 3) == 2
assert min_flips([0, 1, 1, 0, 0, 1, 0], 2) == 0