题目:
给你一个数组 nums
和一个整数 k
。你需要找到 nums
的一个 子数组 ,满足子数组中所有元素按位或运算 OR
的值与 k
的 绝对差 尽可能 小 。换言之,你需要选择一个子数组 nums[l..r]
满足 |k - (nums[l] OR nums[l + 1] ... OR nums[r])|
最小。
请你返回 最小 的绝对差值。
子数组 是数组中连续的 非空 元素序列。
示例 1:
输入:nums = [1,2,4,5], k = 3
输出:0
解释:子数组 nums[0..1] 的按位 OR 运算值为 3 ,得到最小差值 |3 - 3| = 0 。
示例 2:
输入:nums = [1,3,1,3], k = 2
输出:1
解释:子数组 nums[1..1] 的按位 OR 运算值为 3 ,得到最小差值 |3 - 2| = 1 。
示例 3:
输入:nums = [1], k = 10
输出:9
解释:只有一个子数组,按位 OR 运算值为 1 ,得到最小差值 |10 - 1| = 9 。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 109
1 <= k <= 109
思路:
怎么计算连续子数组的 OR?
首先,我们有如下 O(n^2) 的暴力算法:
外层循环,从 i=0 开始,从左到右遍历 nums。内层循环,从 j=i−1 开始,从右到左遍历 nums,更新 nums[j]=nums[j] ∣ nums[i]。
i=1 时,我们会把 nums[0] 到 nums[1] 的 OR 记录在 nums[0] 中。
i=2 时,我们会把 nums[1] 到 nums[2] 的 OR 记录在 nums[1] 中,nums[0] 到 nums[2] 的 OR 记录在 nums[0] 中。
i=3 时,我们会把 nums[2] 到 nums[3] 的 OR 记录在 nums[2] 中;nums[1] 到 nums[3] 的 OR 记录在 nums[1] 中;nums[0] 到 nums[3] 的 OR 记录在 nums[0] 中。
依此类推。
按照该算法,可以计算出所有子数组的 OR。注意单个元素也算子数组。
暴力算法没有充分利用 OR 运算的性质。为了优化,我们需要考察上述过程中,这些元素之间有什么关系。
代码:
class Solution {
public:
int minimumDifference(vector<int>& nums, int k) {
int ans = INT_MAX;
for (int i = 0; i < nums.size(); i++) {
int x = nums[i];
ans = min(ans, abs(x - k));
for (int j = i - 1; j > -1; j--) {
int y = nums[j];
nums[j] |= x;
ans = min(ans, abs(nums[j] - k));
if ((x | y) == y) {
// 可以直接退出内层循环
break;
}
}
}
return ans;
}
};