【LeetCode】309c:最长优雅子数组


原题链接: 309c:最长优雅子数组

题目大意

给你一个由 整数组成的数组 nums

如果 nums 的子数组中位于 不同 位置的每对元素按位(AND)运算的结果等于 0 ,则称该子数组为 优雅 子数组。

返回 最长 的优雅子数组的长度。

子数组 是数组中的一个 连续 部分。

**注意:**长度为 1 的子数组始终视作优雅子数组。
示例 1:

输入:nums = [1,3,8,48,10]
输出:3
解释:最长的优雅子数组是 [3,8,48] 。子数组满足题目条件:
- 3 AND 8 = 0
- 3 AND 48 = 0
- 8 AND 48 = 0
可以证明不存在更长的优雅子数组,所以返回 3 。

示例 2:

输入:nums = [3,1,5,11,13]
输出:1
解释:最长的优雅子数组长度为 1 ,任何长度为 1 的子数组都满足题目条件。

数据范围:

1 <= nums.length <= 10^5
1 <= nums[i] <= 10^9

思路

周赛时用的DP做法,但仍然较为暴力。用状态f[i]表示以位置i为结尾的最长优雅子数组s的长度。那么对于新的位置i+1,只需要对比nums[i + 1]s每个数的与状态(check()函数),若符合要求则尝试更新f[i+1],直接break;若不符合要求,则逐渐从s中抛弃左边的部分数字,对比nums[i + 1]s右子串每个数的与状态,继续尝试更新f[i+1],有更新可直接break。

代码

class Solution {
public:
    bool check(vector<int>& nums, int l, int r){
        for(int i = l; i < r; i ++ ){
            if((nums[r] & nums[i]) == 0) continue;
            else return false;
        }
        
        return true;
    }
    
    int longestNiceSubarray(vector<int>& nums) {
        int n = nums.size();
        const int N = n + 10;
        int res = 1;
        int f[N];
        for(int i = 0; i < n; i ++ ) f[i] = 1;
        
        for(int i = 1; i < n; i ++ ){
            for(int j = i - 1 - f[i - 1] + 1; j <= i; j ++ ){
                if(check(nums, j, i )){
                    f[i] = max(f[i], i - j + 1);
                    break;
                }
            }
            
           
        }
        for(int i = 0; i < n; i ++ ){
            res = max(res, f[i]);
        }
        
        return res;

    }
};

时间复杂度

最差为 O ( n k 2 ) O(nk^2) O(nk2),其中k为以每个位置为结尾的最长优雅数组的平均长度,最好为 O ( n k ) O(nk) O(nk)

思路2

滑动窗口,用状态变量维护窗口内的合法性。其中状态变量可以用数组或一个数(位运算)。
由于数的范围在1到10^9,可用31位二进制数来表示状态。可直接开状态数组cnt[40]来表示当前窗口内的各个位上的1的数量,若符合多个数与为0的要求,则每个位置上的1的数量不超过1。其中双指针i一直往前走,j在左边不断维护窗口合法性,若不合法则往右移动,移动结束后j到i这段区间满足要求,每次更新答案(res = max(res, i - j + 1))。
若用一个int变量表示状态,可使用位运算。

代码2

class Solution {
public:
    int longestNiceSubarray(vector<int>& nums) {
        int cnt[40] = {0};
        int res = 0;
        for(int i = 0, j = 0, tot = 0; i < nums.size(); i ++ ){
            for(int k = 0; k < 31; k ++ ){
                if(nums[i] >> k & 1){
                    if(++ cnt[k] > 1) tot ++;
                }
            }
            while(tot){
                for(int k = 0; k < 31; k ++ ){
                    if(nums[j] >> k & 1){
                        if(-- cnt[k] == 1) tot -- ;
                    }
                }
                j ++ ;
            }

            res = max(res, i - j + 1);
        }

        return res;
    }
};
// 位运算
class Solution {
public:
    int longestNiceSubarray(vector<int>& nums) {
        int cnt[40] = {0};
        int res = 0;
        for(int i = 0, j = 0, state = 0; i < nums.size(); i ++ ){
            while(state & nums[i]){
            	// j++,从state中去除nums[j]的1
                state ^= nums[j ++ ]; 
            }
            state |= nums[i]; // state加入nums[i]

            res = max(res, i - j + 1);
        }

        return res;
    }
};
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值