青训营-小E的按位与挑战

13 阅读2分钟

问题描述

小E有一个长度为n的数组,她想从中选择一个或多个数,使得这些数的按位与(AND)的结果不为0,并且这个结果可以被2^m整除。她的目标是选取这样的数,使得整数m尽可能地大。

你需要帮助小E找到能够使得m最大化的子集。


测试样例

样例1:

输入:n = 5, a = [1, 2, 3, 20, 28]
输出:2

样例2:

输入:n = 4, a = [16, 8, 4, 2]
输出:4

样例3:

输入:n = 6, a = [7, 14, 28, 56, 112, 224]
输出:5

问题分析

  1. 按位与操作的结果特性

    • 如果我们选取多个数进行按位与操作,那么结果中的尾部零的数量(即结果能被 2m2^m2m 整除的最大 mmm)是由所有数的最低尾部零数量决定的。
    • 例如,如果数组中所有数的最低尾部零数量是3,那么按位与结果的尾部零数也至少是3。
  2. 目标

    • 找到一个子集,使得按位与的结果具有尽可能多的尾部零。
    • 尾部零的数量可以通过计算每个数的二进制表示中尾部的连续零的数量来确定。

解题思路

  1. 统计每个数的尾部零数

    • 对于每个数,计算它的尾部连续零的数量。这可以通过将数逐步除以2直到不再是偶数来实现。
  2. 寻找最大尾部零数的子集

    • 计算出数组中每个数的尾部零数后,找到其中的最大值即为最大可能的 mmm 值。

算法实现

def solution(n: int, a: list) -> int:
    # write code here
    def trailing_zeros(x):
        # 计算一个数的尾部零数
        count = 0
        while x % 2 == 0:
            x //= 2
            count += 1
        return count
 # 计算所有数的尾部零数并找出最大值
    max_zeros = 0
    for num in a:
        max_zeros = max(max_zeros, trailing_zeros(num))
    
    return max_zeros
    if __name__ == '__main__':
    print(solution(5, [1, 2, 3, 20, 28]) == 2)
    print(solution(4, [16, 8, 4, 2]) == 4)
    print(solution(6, [7, 14, 28, 56, 112, 224]) == 5)

解释代码

  1. solution 函数:计算一个数的尾部零的数量。
  2. 主函数逻辑:对数组中每个数计算其尾部零数量,找出其中的最大值,即可以使得按位与结果的最大尾部零数量。

复杂度分析

  • 时间复杂度:由于需要遍历数组 a,时间复杂度为 O(nlog⁡V)O(n \log V)O(nlogV),其中 VVV 是数组中数的大小,用于计算每个数的尾部零数。
  • 空间复杂度:只使用常量空间,复杂度为 O(1)O(1)O(1)。