📜  所有子数组的按位与之和(1)

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

所有子数组的按位与之和

在这个问题中,给定一个正整数数组 nums,我们需要找到所有子数组的按位与之和。

问题描述

给定一个正整数数组 nums,它包含 n 个不同的元素,其中每个元素 nums[i] 的值都是非负整数。找到所有子数组的按位与之和。

解题思路

一种直观的解法是暴力枚举所有子数组,然后对每个子数组进行按位与操作,最后求和。但是,这个做法的时间复杂度达到了 O(n^3),因此不适用于该问题。

更优秀的解法是:我们可以依次枚举每一位,考虑在该位上,所有数的按位与之和是多少。我们可以发现,如果对于某一位,存在一个数 k,它在该位上的值为 0,那么所有在这一位上值为 1 的数的按位与之和肯定也为 0。因此,我们只需要找到那些在该位上的值都为 1 的数,然后进行按位与操作即可。

代码实现

我们可以使用两个指针 left 和 right,它们分别表示当前考虑的子数组的左右端点。对于每个 left,我们让 right 向右移动,直到所有数在当前位上的值都为 1,然后计算当前子数组按位与之和,最后将右指针 right 移回 left 的位置。

这样,我们就可以得到所有子数组的按位与之和。

具体的代码实现如下所示:

def sum_of_bitwise_and(nums: List[int]) -> int:
    res = 0
    n = len(nums)

    for i in range(30):
        left, right = 0, 0
        mask = 1 << i
        while right < n:
            if nums[right] & mask:
                right += 1
            else:
                res += (right - left) * (right - left + 1) // 2 * mask
                left = right + 1
                right = left
        if left < n:
            res += (n - left) * (n - left + 1) // 2 * mask

    return res

其中,我们使用了位运算和数学公式相结合的方法,让代码更加简洁明了。具体来说:

  • mask 表示当前考虑的位。
  • nums[right] & mask 表示 right 对应的数在当前位上的值。
  • right - left 表示以 right 为右端点且当前位上的值都为 1 的子数组的个数。
  • right - left + 1 表示以 right 为右端点但当前位上的值不全为 1 的子数组的个数。
  • (right - left) * (right - left + 1) // 2 表示以 right 为右端点且当前位上的值都为 1 的子数组的按位与之和。
  • (n - left) * (n - left + 1) // 2 表示以 left 为左端点且当前位上的值都为 1 的子数组的按位与之和。注意,right 已经统计了包含 left 的所有子数组的情况,因此我们只需要计算不包含 left 的子数组即可。
总结

这个问题可以使用位运算和双指针的方法解决。我们需要依次枚举每一位,然后对于每一位,使用双指针分别表示当前考虑的子数组的左右端点,统计所有子数组在当前位上的按位与之和。时间复杂度为 O(nlogC),其中 C 表示数组中所有数的最大值。