📅  最后修改于: 2023-12-03 15:40:16.755000             🧑  作者: Mango
给定一个非负整数数组和一个整数k,找到长度最小的子数组,该子数组的按位与值大于等于k。如果不存在这样的子数组,则返回0。
示例1:
输入: [10,5,4,3,7,8], k = 7
输出: 2
解释: 子数组 [4,3] 的按位与值是 4 & 3 = 0b100 & 0b011 = 0b000,该值为4,大于等于7,且长度最小。
示例2:
输入: [1,2,4,0,0,3,2,2,1,0], k = 7
输出: 0
解释: 数组的任何子数组的按位与都小于7,因此返回0。
我们可以发现,对于一个长度为n的数组来说,其所有子数组的个数为$O(n^2)$,所以直接遍历寻找符合条件的子数组是无法通过所有测试用例的。
对于本题,我们可以通过位运算来优化算法。我们来看一下按位与的性质:
因此,我们可以从高到低枚举每一位的值,如果当前位为0,则不管它,如果当前位为1,则寻找子数组的过程中,直接排除所有该位为0的数,具体而言,从数组第一位开始,一旦找到当前位为0的数,就直接跳过,节省了不少时间。
另外,我们可以使用一个值temp来记录当前子数组的按位与值,如果一个数x与当前的temp按位与值小于k,那么我们可以跳过从该数开始的所有子数组,因为这些子数组的按位与值一定小于k。
最后,我们需要注意一下边界情况,如果遍历完整个数组后,仍没有满足条件的子数组,则返回0。
class Solution:
def minSubArrayLen(self, nums: List[int], k: int) -> int:
n = len(nums)
ans = float('inf')
temp = 0
for i in range(32, -1, -1):
# 记录当前子数组的按位与值
temp |= (1 << i)
# 表示子数组的右端点
cur = 0
# 表示当前子数组的按位与值
cur_sum = 0
for j in range(n):
# 只要该数当前位为0,就跳过
if (nums[j] & temp) == 0:
cur = j + 1
cur_sum = 0
continue
# 更新cur_sum
cur_sum |= nums[j]
# 如果 cur_sum 大于 k,更新 ans,并缩小子数组范围
if cur_sum >= k:
ans = min(ans, j - cur + 1)
break
return ans if ans != float('inf') else 0
时间复杂度:$O(n\log^2C)$,其中$C$为数组中的元素值的最大位数。
空间复杂度:$O(1)$。