找到按位或最接近K的子数组

2024-10-09

题目:

给你一个数组 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 运算的性质。为了优化,我们需要考察上述过程中,这些元素之间有什么关系。

image-20241009093632995

代码:

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;
    }
};