Medium
题目描述
给你一个非负整数数组 nums 和一个整数 k。
如果一个数组的所有元素按位或运算的结果至少为 k,那么这个数组被称为特殊数组。
返回 nums 中最短的特殊非空子数组的长度,如果不存在特殊子数组,则返回 -1。
示例 1:
输入:nums = [1,2,3], k = 2
输出:1
解释:子数组 [3] 的或值为 3。因此,返回 1。
示例 2:
输入:nums = [2,1,8], k = 10
输出:3
解释:子数组 [2,1,8] 的或值为 11。因此,返回 3。
示例 3:
输入:nums = [1,2], k = 0
输出:1
解释:子数组 [1] 的或值为 1。因此,返回 1。
约束条件:
1 <= nums.length <= 2 * 10^50 <= nums[i] <= 10^90 <= k <= 10^9
解题思路
解题思路
这道题需要找到按位或值至少为 k 的最短子数组。关键观察是按位或运算的性质:只会设置新的位,永远不会取消已设置的位。
核心思想
- 状态维护:对于每个位置 i,我们需要维护所有以 i 结尾的子数组的按位或值。
- 去重优化:由于按位或的单调性,以某个位置结尾的所有子数组的按位或值最多只有 32 种不同的值(对应 32 个二进制位)。
- 状态转移:从位置 i-1 的状态转移到位置 i 时,将 nums[i] 与所有之前的或值进行或运算。
算法流程
- 维护一个集合,存储
(或值, 对应的最短长度)对 - 对于每个元素 nums[i]:
- 将 nums[i] 本身作为长度为 1 的子数组加入
- 对之前的所有或值与 nums[i] 进行或运算,更新状态
- 去除重复的或值,只保留最短长度
- 检查是否有或值 ≥ k,更新答案
这种方法利用了按位或的单调性,将时间复杂度优化到了 O(n log max(nums))。
代码实现
class Solution {
public:
int minimumSubarrayLength(vector<int>& nums, int k) {
if (k == 0) return 1;
int n = nums.size();
int result = INT_MAX;
// 存储 (or_value, min_length) 对
vector<pair<int, int>> prev;
for (int i = 0; i < n; i++) {
vector<pair<int, int>> curr;
// 添加当前元素作为单独的子数组
curr.push_back({nums[i], 1});
// 扩展之前的子数组
for (auto& p : prev) {
int newOr = p.first | nums[i];
curr.push_back({newOr, p.second + 1});
}
// 去重,保留最短长度
prev.clear();
for (auto& p : curr) {
if (p.first >= k) {
result = min(result, p.second);
}
// 只保留不重复的or值,且保留最短长度
bool found = false;
for (auto& existing : prev) {
if (existing.first == p.first) {
existing.second = min(existing.second, p.second);
found = true;
break;
}
}
if (!found) {
prev.push_back(p);
}
}
}
return result == INT_MAX ? -1 : result;
}
};
class Solution:
def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
if k == 0:
return 1
result = float('inf')
prev = [] # 存储 (or_value, min_length) 对
for num in nums:
curr = [(num, 1)] # 当前元素作为单独子数组
# 扩展之前的子数组
for or_val, length in prev:
new_or = or_val | num
curr.append((new_or, length + 1))
# 去重,保留最短长度
prev = []
seen = {}
for or_val, length in curr:
if or_val >= k:
result = min(result, length)
if or_val not in seen:
seen[or_val] = length
else:
seen[or_val] = min(seen[or_val], length)
prev = list(seen.items())
return result if result != float('inf') else -1
public class Solution {
public int MinimumSubarrayLength(int[] nums, int k) {
if (k == 0) return 1;
int result = int.MaxValue;
var prev = new List<(int orValue, int length)>();
foreach (int num in nums) {
var curr = new List<(int, int)> { (num, 1) };
// 扩展之前的子数组
foreach (var (orValue, length) in prev) {
int newOr = orValue | num;
curr.Add((newOr, length + 1));
}
// 去重,保留最短长度
prev.Clear();
var seen = new Dictionary<int, int>();
foreach (var (orValue, length) in curr) {
if (orValue >= k) {
result = Math.Min(result, length);
}
if (!seen.ContainsKey(orValue)) {
seen[orValue] = length;
} else {
seen[orValue] = Math.Min(seen[orValue], length);
}
}
foreach (var kvp in seen) {
prev.Add((kvp.Key, kvp.Value));
}
}
return result == int.MaxValue ? -1 : result;
}
}
var minimumSubarrayLength = function(nums, k) {
if (k === 0) return 1;
let n = nums.length;
let minLength = Infinity;
for (let i = 0; i < n; i++) {
let or = 0;
for (let j = i; j < n; j++) {
or |= nums[j];
if (or >= k) {
minLength = Math.min(minLength, j - i + 1);
break;
}
}
}
return minLength === Infinity ? -1 : minLength;
};
复杂度分析
| 复杂度类型 | 复杂度分析 |
|---|---|
| 时间复杂度 | O(n × log(max(nums))),其中 n 是数组长度。由于按位或的单调性,每个位置最多维护 32 种不同的或值 |
| 空间复杂度 | O(log(max(nums))),存储不同或值和对应长度的空间 |