二进制尾零子数组问题 | 豆包MarsCode AI 刷题

66 阅读3分钟

二进制尾零子数组问题 | 豆包MarsCode AI 刷题

问题描述

小U拿到了一个由 nn 个正整数组成的数组,她需要从中选择一个连续子数组,使得该子数组中所有元素的乘积在二进制表示中末尾至少有 kk 个连续的 00。也就是说,子数组的乘积必须是 2k2^k 的倍数。你的任务是找到满足条件的最短连续子数组的长度。如果不存在这样的子数组,输出 1−1

例如,给定数组 [1, 2, 3, 4, 5, 6] 和 k=3k=3,你需要找到一个连续子数组,它的乘积在二进制中尾部至少有3个 00。取子数组 [2, 3, 4] 时,乘积 2×3×4=242×3×4=24,其二进制表示为 11000,尾部有3个 '0'。

思路

最简单的思路,直接暴力求解,长度从1n遍历连续子数组,然后检测每个子数组的成绩是否是2k2^k的倍数,如果是直接返回。如果找不到返回-1

def solution(n: int, k: int, a: list) -> int:
    target = 2 ** k

    for length in range(1, n + 1):
        for start in range(n - length + 1):
            product = 1

            for i in range(start, start + length):
                product *= a[i]
                
            if product % target == 0:
                return length

    return -1

if __name__ == '__main__':
    #test cases...

再分析一下,如果一个数字能被 2k2^k 整除,则其尾部有至少 kk 个连续的二进制 00。若子数组的乘积满足此条件,意味着该子数组中 22 的因子总数至少为 kk

那对于数组中的每个数,我们可以先计算出该数能分解出多少个 22 。这样,我们可以问题转化成“求 22 的因子数的和等于 kk 的最小连续子数组的长度”

比如,对于样例

输入:n = 6,k = 3,a = [1, 2, 3, 4, 5, 6]
输出:3

先求每个数的 22 的因子个数,可以得到数组b = [0, 1, 0, 2, 0, 1]。然后可以很容易看出子数组[1, 0, 2]或者[2, 0, 1]满足我们的要求。

对于因子数的和的计算,我们可以使用滑动窗口或者双指针来找。

代码:
def solution(n: int, k: int, a: list) -> int:
    def count_twos(x):
        count = 0
        while x % 2 == 0:
            count += 1
            x //= 2
        return count

    # 计算每个元素的因子数量
    twos = [count_twos(x) for x in a]

    # 滑动窗口寻找最短满足条件的子数组
    left = 0
    current_sum = 0
    min_length = float('inf')

    for right in range(n):
        current_sum += twos[right]

        # 如果当前窗口内因子和大于等于 k,尝试收缩窗口
        while current_sum >= k:
            min_length = min(min_length, right - left + 1)
            current_sum -= twos[left]
            left += 1

    # 如果未找到满足条件的子数组,返回 -1
    return min_length if min_length != float('inf') else -1

if __name__ == '__main__':
    #test cases...

更短的代码,加上了位运算优化:

def solution(n: int, k: int, a: list) -> int:
    s = l = 0
    r = float('inf')
    for i, x in enumerate(a):
        s += (x & -x).bit_length() - 1
        while s >= k: r, s, l = min(r, i - l + 1), s - ((a[l] & -a[l]).bit_length() - 1), l + 1
    return r if r < float('inf') else -1
    
if __name__ == '__main__':
    #test cases...

复杂度分析

  • 时间复杂度

    • 预处理因子数量:O(nlog(max(a)))O(nlog⁡(max(a))),其中 max(a)max(a) 是数组 aa 中最大值。
    • 滑动窗口:O(n)O(n)
    • 总时间复杂度:O(nlog(max(a)))O(nlog⁡(max(a)))
  • 空间复杂度

    • 额外的数组 twostwos,空间复杂度为 O(n)O(n)