Hard
题目描述
温斯顿得到了上述神秘函数 func。他有一个整数数组 arr 和一个整数 target,他想找到使得 |func(arr, l, r) - target| 尽可能小的 l 和 r 值。
返回 |func(arr, l, r) - target| 的最小可能值。
注意 func 应该在满足 0 <= l, r < arr.length 的 l 和 r 值下调用。
示例 1:
输入:arr = [9,12,3,7,15], target = 5
输出:2
解释:对所有 [l,r] 对 [[0,0],[1,1],[2,2],[3,3],[4,4],[0,1],[1,2],[2,3],[3,4],[0,2],[1,3],[2,4],[0,3],[1,4],[0,4]] 调用 func,温斯顿得到结果 [9,12,3,7,15,8,0,3,7,0,0,3,0,0,0]。最接近 5 的值是 7 和 3,因此最小差值是 2。
示例 2:
输入:arr = [1000000,1000000,1000000], target = 1
输出:999999
解释:温斯顿对所有可能的 [l,r] 值调用 func,总是得到 1000000,因此最小差值是 999999。
示例 3:
输入:arr = [1,2,4,8,16], target = 0
输出:0
约束:
- 1 <= arr.length <= 10^5
- 1 <= arr[i] <= 10^6
- 0 <= target <= 10^7
提示:
- 子数组 arr[i…j] 的 AND 值 ≥ 子数组 arr[i…j+1] 的 AND 值
- 对于每个索引 i,使用二分搜索或三分搜索找到索引 j,使得 |target - AND(arr[i…j])| 最小,用这个值更新全局答案。
解题思路
这道题的核心是理解位运算 AND 操作的单调性质:随着子数组长度增加,AND 值只会减少或保持不变。
关键观察:
- 对于固定的左端点 i,右端点 j 向右扩展时,AND(arr[i..j]) 单调递减
- 这意味着对于每个起始位置 i,所有可能的 AND 值构成一个递减序列
- 由于数组元素最大为 10^6(约20位),每个位置最多产生20个不同的 AND 值
算法思路: 我们可以用集合来维护所有可能的 AND 值。遍历数组时:
- 对于当前元素 arr[i],更新之前所有可能的 AND 值
- 将当前元素本身也加入候选集合
- 用新的 AND 值集合更新全局最小差值
优化点:
- 使用集合去重,避免重复计算相同的 AND 值
- 利用 AND 操作的单调性,每次最多产生有限个不同值
这种方法的时间复杂度实际上是 O(n log(max_val)),因为每个位置最多产生 log(max_val) 个不同的 AND 值。
代码实现
class Solution {
public:
int closestToTarget(vector<int>& arr, int target) {
set<int> candidates;
int result = INT_MAX;
for (int num : arr) {
set<int> newCandidates;
newCandidates.insert(num);
for (int candidate : candidates) {
newCandidates.insert(candidate & num);
}
for (int candidate : newCandidates) {
result = min(result, abs(candidate - target));
}
candidates = newCandidates;
}
return result;
}
};
class Solution:
def closestToTarget(self, arr: List[int], target: int) -> int:
candidates = set()
result = float('inf')
for num in arr:
new_candidates = {num}
for candidate in candidates:
new_candidates.add(candidate & num)
for candidate in new_candidates:
result = min(result, abs(candidate - target))
candidates = new_candidates
return result
public class Solution {
public int ClosestToTarget(int[] arr, int target) {
HashSet<int> candidates = new HashSet<int>();
int result = int.MaxValue;
foreach (int num in arr) {
HashSet<int> newCandidates = new HashSet<int>();
newCandidates.Add(num);
foreach (int candidate in candidates) {
newCandidates.Add(candidate & num);
}
foreach (int candidate in newCandidates) {
result = Math.Min(result, Math.Abs(candidate - target));
}
candidates = newCandidates;
}
return result;
}
}
var closestToTarget = function(arr, target) {
let candidates = new Set();
let result = Infinity;
for (let num of arr) {
let newCandidates = new Set();
newCandidates.add(num);
for (let candidate of candidates) {
newCandidates.add(candidate & num);
}
for (let candidate of newCandidates) {
result = Math.min(result, Math.abs(candidate - target));
}
candidates = newCandidates;
}
return result;
};
复杂度分析
| 复杂度类型 | 复杂度 | 说明 |
|---|---|---|
| 时间复杂度 | O(n log(max_val)) | n 为数组长度,max_val 为数组元素最大值,每个位置最多产生 log(max_val) 个不同的 AND 值 |
| 空间复杂度 | O(log(max_val)) | 集合中最多存储 log(max_val) 个不同的 AND 值 |