📌  相关文章
📜  计数由具有恰好K个设置位的元素组成的子数组(1)

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

计数由具有恰好K个设置位的元素组成的子数组

在这个问题中,我们需要计算由恰好K个设置位的元素组成的子数组的数量。一个设置位是指二进制数中的1位。

例如,对于数组[1, 3, 5, 7]和K = 2,我们可以找到以下子数组:

  • [1, 3]
  • [1, 3, 5]
  • [1, 3, 5, 7]
  • [3, 5]
  • [3, 5, 7]
  • [5, 7]

这里有一种原始的暴力解决方案:

def count_subarrays_with_k_set_bits(arr, K):
    count = 0
    for i in range(len(arr)):
        for j in range(i, len(arr)):
            n_set_bits = bin(arr[i:j+1]).count('1')
            if n_set_bits == K:
                count += 1
    return count

这个解决方案的时间复杂度为O(n^3),因为我们有两个循环(O(n^2))并且在内部循环中,我们使用bin函数来计算一个子数组中的设置位数(O(n))。

一个更好的解决方案是通过维护一个计数器来跟踪当前子数组中的设置位数。我们可以使用二进制“位运算”来计算一个数中的设置位数:

def count_set_bits(n):
    count = 0
    while n > 0:
        count += n & 1
        n >>= 1
    return count

这个函数使用一个while循环来检查n的最低位是否为1。每次循环后,将n右移一位,直到n为0为止。

现在我们可以使用这个函数来计算一个数组中的子段的设置位数:

def count_subarrays_with_k_set_bits(arr, K):
    count = 0
    n_set_bits = 0
    freq = [0] * 32  # 打表预处理
      
    freq[n_set_bits] += 1
    for i in range(len(arr)):
        n_set_bits += count_set_bits(arr[i])
        if n_set_bits >= K:
            count += freq[n_set_bits - K]
        freq[n_set_bits] += 1
             
    return count

我们添加了一个计数器n_set_bits和一个数组freq,用于跟踪直到当前索引为止,每个设置位数值的计数器。一旦我们达到K个设置位,我们可以将freq[n_set_bits - K]添加到计数器中。

这个算法的时间复杂度为O(nlogn),因为我们在每个i循环时调用count_set_bits函数。

一个更可取的解决方案是使用双指针来保持一个当前子数组。我们将start指针指向当前子数组的起始位置,end指针指向结束位置。

def count_subarrays_with_k_set_bits(arr, K):
    count = 0
    n_set_bits = 0
    freq = [0] * 32
      
    start, end = 0, 0

    while end < len(arr):
        n_set_bits += count_set_bits(arr[end])
          
        while n_set_bits > K and start <= end:
            n_set_bits -= count_set_bits(arr[start])
            start += 1

        if n_set_bits == K:
            count += 1
            freq[n_set_bits] += 1
            end += 1
        elif n_set_bits < K:
            end += 1
        else:  # n_set_bits > K
            n_set_bits -= count_set_bits(arr[start])
            start += 1
  
    return count

在这个解决方案中,我们在每次循环中维护current_sum并使用while循环来检查current_sum是否大于K。如果是,我们将start指向的元素从当前子数组中删除,否则我们将end指针向前移动直到达到数组的末尾。如果current_sum等于K,则我们找到了一个新的符合条件的子数组并增加计数器。

这个算法的时间复杂度为O(n),因为我们只对每个元素进行一次迭代。