Facebook Pixel

3171. Find Subarray With Bitwise OR Closest to K


Problem Description

You are given an array nums and an integer k. You need to find a subarray of nums such that the absolute difference between k and the bitwise OR of the subarray elements is as small as possible. In other words, select a subarray nums[l..r] such that |k - (nums[l] OR nums[l + 1] ... OR nums[r])| is minimum.

Return the minimum possible value of the absolute difference.

A subarray is a contiguous non-empty sequence of elements within an array.

Intuition

To solve this problem, we use the two-pointer technique combined with bitwise operations. The main idea is to leverage the properties of the bitwise OR operation, where adding more numbers to a subarray can only increase the OR value or keep it the same.

  1. Bitmask Representation: Each integer in the array has a bitwise representation. We track which bits are set (have the value 1) as we iterate through nums.

  2. Fixed Right Endpoint: By fixing the right endpoint of a current subarray and trying to expand from left to right, we can efficiently calculate the OR for this segment.

  3. Shrink if Necessary: If the OR value exceeds k, we reduce the size of the subarray from the left, maintaining an optimal solution by using a counter to see if all bits could be eliminated to explore smaller OR results.

  4. Optimization: The goal is to minimize |current_OR - k| during each iteration. The use of a counter array allows effective maintenance of currently set bits when tweaking the left endpoint.

The approach explores all potential subarrays and calculates the OR values efficiently, ensuring that whenever the current OR goes over k, the process quickly recalibrates to find smaller, acceptable solutions.

This method efficiently utilizes bitwise operations and shifting with emphasis on maintaining a minimized computational overhead by addressing only necessary bit positions.

Learn more about Segment Tree and Binary Search patterns.

Solution Approach

The solution employs a combination of the two-pointer technique and bitwise operations to tackle the problem efficiently. Here's how the algorithm is structured:

  1. Initialization:

    • The variable m is determined by finding the bit length of the maximum number in nums. This represents the number of bits we may need to consider.
    • An array cnt of size m is initialized to track the count of subarray elements where each bit is set.
    • Other variables like s (current OR value), i (left pointer), and ans (the answer) are also initialized.
  2. Iterating Over Elements:

    • The for loop iterates with j as the right endpoint of the subarray.
    • s |= x updates the current OR value by including the element x = nums[j].
    • ans is updated with ans = min(ans, abs(s - k)) to track the minimum difference.
  3. Adjusting the Left Endpoint:

    • If the current OR s exceeds k, the left pointer i is advanced until s is no longer greater than k.
    • For each bit index h, if the current element y = nums[i] has the h-th bit set, decrement cnt[h].
    • When cnt[h] is zero, it implies no remaining elements in the current subarray have the h-th bit set, so we can safely exclude it from s using s ^= 1 << h.
    • Update ans after each adjustment.
  4. Return Result:

    • After traversing the array, return ans, which represents the minimum absolute difference found.

By adjusting subarray boundaries with the two-pointer approach and carefully managing bitwise operations, the algorithm efficiently computes the required minimum difference using O(n * log M) time complexity where n is the number of elements and M is the maximum element value in terms of bits.

Ready to land your dream job?

Unlock your dream job with a 2-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through an example to better understand how the solution approach works.

Input Example:

  • nums = [1, 2, 4, 8]
  • k = 5

Objective: Find a subarray whose bitwise OR is as close as possible to k.

Step-by-step Walkthrough:

  1. Initialization:

    • Determine the maximum value in nums to calculate its bit length. Here, the max number is 8 with a bit length of 4.
    • Initialize cnt as [0, 0, 0, 0] to keep track of set bits at each position.
    • Initialize s = 0 (current OR value), i = 0 (left pointer), and ans = |0 - 5| = 5.
  2. Iterate Over Elements:

    • j = 0, x = nums[0] = 1:

      • Compute s |= 1, updating s to 1.
      • ans becomes min(5, |1 - 5|) = 4.
    • j = 1, x = nums[1] = 2:

      • Compute s |= 2, updating s to 3.
      • ans becomes min(4, |3 - 5|) = 2.
    • j = 2, x = nums[2] = 4:

      • Compute s |= 4, updating s to 7.
      • ans remains min(2, |7 - 5|) = 2.
  3. Adjust Left Pointer When OR Exceeds k:

    • Since s (7) is greater than k (5), increment i until s is manageable.
    • Remove nums[0] (1):
      • Update cnt and s accordingly. Decrement cnt for any set bits in 1.
      • No change in s since bit positions are still covered by other elements in the subarray.
    • Remove nums[1] (2):
      • Update cnt for nums[1], and since all bits in 2 are set, reduce current OR. Thus s becomes 4.
      • ans updates to min(2, |4 - 5|) = 1.
  4. j = 3, x = nums[3] = 8:

    • Compute s |= 8, updating s to 12.
    • ans remains 1 as |k - s| is larger.
  5. Final Adjustments:

    • Adjust left pointer i further if necessary, to find other subarrays maintaining close OR values.
    • Removing elements further doesn't improve ans.

Return Result:

  • The minimum absolute difference found is 1.

This walkthrough illustrates how each element and bit operation influences the result and how the two-pointer technique helps efficiently manage subarray boundaries to optimize calculations.

Solution Implementation

1from typing import List
2from math import inf
3
4class Solution:
5    def minimumDifference(self, nums: List[int], k: int) -> int:
6        max_bits = max(nums).bit_length()  # Determine the number of bits needed to represent the maximum number in nums
7        bit_count = [0] * max_bits  # Array to count set bits at each position
8        current_or = 0  # Variable to store the OR of current elements
9        left = 0  # Left pointer for window
10        min_difference = inf  # Initialize the minimum difference with infinity
11
12        # Iterate through each number in nums with index
13        for right, num in enumerate(nums):
14            current_or |= num  # Include the current number in the OR
15            # Update the minimum difference
16            min_difference = min(min_difference, abs(current_or - k))
17          
18            # Update bit count for the current number
19            for bit_position in range(max_bits):
20                if num >> bit_position & 1:
21                    bit_count[bit_position] += 1
22          
23            # Adjust the window size to ensure OR value does not exceed k
24            while left < right and current_or > k:
25                left_num = nums[left]
26                # Roll back the changes for the leftmost number since it's being removed from the window
27                for bit_position in range(max_bits):
28                    if left_num >> bit_position & 1:
29                        bit_count[bit_position] -= 1
30                        if bit_count[bit_position] == 0:
31                            # Remove the bit from the OR if no more numbers set it
32                            current_or ^= 1 << bit_position
33                left += 1  # Move the left pointer to the right
34                # Update the minimum difference
35                min_difference = min(min_difference, abs(current_or - k))
36      
37        return min_difference  # Return the calculated minimum difference
38
1class Solution {
2    public int minimumDifference(int[] nums, int k) {
3        // Find the maximum value in the array to determine the number of bits needed.
4        int maxNum = 0;
5        for (int num : nums) {
6            maxNum = Math.max(maxNum, num);
7        }
8      
9        // Determine the number of bits needed to represent the maximum number.
10        int numBits = 32 - Integer.numberOfLeadingZeros(maxNum);
11      
12        // Array to count occurrences of bits.
13        int[] bitCount = new int[numBits];
14        int n = nums.length;
15        int minDifference = Integer.MAX_VALUE;
16      
17        // Initialize pointers i, j and variable s to 0.
18        for (int i = 0, j = 0, bitwiseOrSum = 0; j < n; ++j) {
19            // Update the bitwise OR sum with the current element.
20            bitwiseOrSum |= nums[j];
21            // Update the minimum difference.
22            minDifference = Math.min(minDifference, Math.abs(bitwiseOrSum - k));
23          
24            // Update the occurrences of each bit in the current number.
25            for (int bitPos = 0; bitPos < numBits; ++bitPos) {
26                if ((nums[j] >> bitPos & 1) == 1) {
27                    ++bitCount[bitPos];
28                }
29            }
30          
31            // Adjust the left pointer i until the condition is satisfied.
32            while (i < j && bitwiseOrSum > k) {
33                for (int bitPos = 0; bitPos < numBits; ++bitPos) {
34                    if ((nums[i] >> bitPos & 1) == 1 && --bitCount[bitPos] == 0) {
35                        // If the bit count is zero, remove this bit from the sum.
36                        bitwiseOrSum ^= 1 << bitPos;
37                    }
38                }
39                ++i;  // Increment the left pointer.
40                minDifference = Math.min(minDifference, Math.abs(bitwiseOrSum - k));
41            }
42        }
43        return minDifference;
44    }
45}
46
1#include <vector>
2#include <algorithm>
3#include <climits>
4
5class Solution {
6public:
7    int minimumDifference(std::vector<int>& nums, int k) {
8        // Find the maximum value in nums
9        int maxNum = *std::max_element(nums.begin(), nums.end());
10      
11        // Calculate the number of bits needed to represent maxNum
12        int bitCount = 32 - __builtin_clz(maxNum);
13      
14        // Get the size of the nums vector
15        int size = nums.size();
16      
17        // Initialize the answer to a large value (infinity substitute)
18        int answer = INT_MAX;
19      
20        // Create a vector to count occurrences of bits
21        std::vector<int> bitCounter(bitCount);
22      
23        // Initialize two pointers for the sliding window approach
24        for (int left = 0, right = 0, currentOrValue = 0; right < size; ++right) {
25            // Update the bitwise OR for the current window
26            currentOrValue |= nums[right];
27          
28            // Calculate the minimum difference found so far
29            answer = std::min(answer, abs(currentOrValue - k));
30          
31            // Count the bits for the current number
32            for (int bit = 0; bit < bitCount; ++bit) {
33                if (nums[right] >> bit & 1) {
34                    ++bitCounter[bit];
35                }
36            }
37          
38            // Adjust the window if the current OR value is greater than k
39            while (left < right && currentOrValue > k) {
40                for (int bit = 0; bit < bitCount; ++bit) {
41                    if (nums[left] >> bit & 1 && --bitCounter[bit] == 0) {
42                        // Remove the influence of this bit from currentOrValue
43                        currentOrValue ^= 1 << bit;
44                    }
45                }
46              
47                // Recalculate the minimum difference after shrinking the window
48                answer = std::min(answer, abs(currentOrValue - k));
49              
50                // Move the left pointer to shrink the window
51                ++left;
52            }
53        }
54        return answer;
55    }
56};
57
1function minimumDifference(nums: number[], k: number): number {
2    // Determine the binary length of the maximum number to track bit positions
3    const maxBinaryLength = Math.max(...nums).toString(2).length;
4    const n = nums.length;
5
6    // Initialize an array to count bit frequencies
7    const bitCount: number[] = Array(maxBinaryLength).fill(0);
8    let result = Infinity; // Start with the largest possible value
9
10    // Use two pointers to form a sliding window
11    for (let start = 0, end = 0, sum = 0; end < n; ++end) {
12        sum |= nums[end]; // Add current number's bits to the sum
13        result = Math.min(result, Math.abs(sum - k)); // Update result with the current difference
14
15        // Update the bit counts for current number
16        for (let bitPosition = 0; bitPosition < maxBinaryLength; ++bitPosition) {
17            if ((nums[end] >> bitPosition) & 1) {
18                ++bitCount[bitPosition];
19            }
20        }
21
22        // Slide the window to reduce sum while it exceeds k
23        while (start < end && sum > k) {
24            // Update bit counts when removing a number from the window (start side)
25            for (let bitPosition = 0; bitPosition < maxBinaryLength; ++bitPosition) {
26                if ((nums[start] >> bitPosition) & 1 && --bitCount[bitPosition] === 0) {
27                    sum ^= 1 << bitPosition; // Toggle the bit if it's no longer present
28                }
29            }
30            result = Math.min(result, Math.abs(sum - k)); // Update result
31            ++start; // Move the start pointer
32        }
33    }
34
35    return result; // Return the smallest difference found
36}
37

Time and Space Complexity

The time complexity of the code is O(n * m), where n is the length of the input list nums, and m is the maximum bit length of any element in nums. This is because for each element in nums, the algorithm potentially iterates over the bit length m to update the cnt array and recalculate s.

The space complexity is O(m), as the cnt list holds up to m elements, which corresponds to the bit length of the maximum number in nums.

Learn more about how to find time and space complexity quickly.


Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:
Question 1 out of 10

Which two pointer techniques do you use to check if a string is a palindrome?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!


Load More