区间或值大于等于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;
    }
};
posted @ 2024-04-02 16:50  lipu123  阅读(34)  评论(0)    收藏  举报