区间或值大于等于k
给你一个 非负 整数数组 nums
和一个整数 k
。
如果一个数组中所有元素的按位或运算 OR
的值 至少 为 k
,那么我们称这个数组是 特别的 。
请你返回 nums
中 最短特别非空子数组的长度,如果特别子数组不存在,那么返回 -1 。
示例 1:
输入:nums = [1,2,3], k = 2
输出:1
解释:
子数组 [3] 的按位 OR 值为 3 ,所以我们返回 1 。
示例 2:
输入:nums = [2,1,8], k = 10
输出:3
解释:
子数组 [2,1,8] 的按位 OR 值为 11 ,所以我们返回 3 。
示例 3:
输入:nums = [1,2], k = 0
输出:1
解释:
子数组 [1] 的按位 OR 值为 1 ,所以我们返回 1 。
提示:
\(1 <= nums.length <= 2 * 10^5\)
\(0 <= nums[i] <= 10^9\)
\(0 <= k <= 10^9\)
这个题:我当时做的前缀和+二分
这个题为什么能二分呢?因为区间或它是具有单调性的,随着区间的增大,它的值只会越来越大。
首先我们二分的时候固定长度,然后再judge函数中,我们枚举起始点,然后最主要的就是如何O(1)
的复杂度解决区间或值。那么这个时候就用到前缀和了,首先\(0 <= nums[i] <= 10^9\),将其二进制分解最多31位,所以我们定义一个二维数组cnt[n][31]
。然后这个cnt[i][j]
表示的是前i个数组中对应二进制第\(j\)位的1的个数,然后我们要求[l,r]的或值的话。就是
for(int j=0;j<31;j++){
int p=cnt[r][j]-cnt[l-1][j];
if(p){
ans+=(2^j);
}
}
代码:
typedef long long ll;
class Solution {
public:
int judge(int x,vector<vector<int>>& cnt,int k,int n){
for(int l=1;l+x-1<=n;l++){//r=l+x-1;
int r=l+x-1;
ll z=1,ans=0;
for(int j=0;j<31;j++){
int p=cnt[r][j]-cnt[l-1][j];
if(p){
ans+=z;
}
z=z*2ll;
}
if(ans>=k){
return 1;
}
}
return 0;
}
int minimumSubarrayLength(vector<int>& nums, int k) {
int n=nums.size();
vector<vector<int>>cnt(n+1,vector<int>(31));
for(int i=1;i<=n;i++){
for(int j=0;j<31;j++){
int t = (nums[i-1] >> j) & 1;
cnt[i][j]=cnt[i-1][j]+t;
}
}
int l=1,r=n,ans=-1;
while(r>=l){
int mid=(l+r)/2;
if(judge(mid,cnt,k,n)){
r=mid-1;
ans=mid;
}
else{
l=mid+1;
}
}
return ans;
}
};