原题链接: 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;
}
};