Facebook Pixel

3095. Shortest Subarray With OR at Least K I


Problem Description

You are given an array nums of non-negative integers and an integer k.

An array is called special if the bitwise OR of all of its elements is at least k.

Return the length of the shortest special non-empty subarray of nums, or return -1 if no special subarray exists.

Intuition

To solve this problem, the two pointers technique offers an efficient way to find the shortest special subarray. The idea is to maintain a window, represented by two pointers i and j, that slides over the array. Here's the detailed thought process:

  1. Understanding the 'Bitwise OR': The OR operation on binary numbers results in a new number where each bit is the OR of the corresponding bits of the operands. Once a bit in the result has been set to 1, it cannot revert to 0 by adding more numbers. Hence, when moving the right endpoint j to the right, the bitwise OR value never decreases.

  2. Initialize Variables: We start with both pointers i (left) and j (right) at the beginning of nums. We use a variable s to track the current bitwise OR of the subarray defined by the two pointers. Additionally, a cnt array of size 32 keeps track of how many numbers in the current subarray contribute to each bit position, allowing us to efficiently adjust s when the left pointer moves.

  3. Expanding the Right Pointer: Increment j and update s to include nums[j]. This involves setting bits in s based on the bits in nums[j].

  4. Contracting the Left Pointer: Once s becomes greater than or equal to k, attempt to shrink the window by moving the left pointer i to the right. During this, adjust s by removing the effect of nums[i], decrementing the corresponding bit counts in cnt. If a bit count drops to zero, it indicates that the bit must be cleared from s.

  5. Recording the Shortest Length: Each time the condition s >= k is met, compare the current window size (j - i + 1) with the previously recorded shortest length and update it if a new shorter length is found.

  6. Result Handling: After iterating through the array, if no subarray satisfied the condition, return -1; otherwise, return the recorded shortest length.

This approach efficiently finds the shortest subarray by leveraging the non-decreasing nature of the OR operation and limiting unnecessary operations to the smallest necessary subarrays.

Learn more about Sliding Window patterns.

Solution Approach

The solution employs a Two Pointers technique combined with Counting to efficiently find the shortest special subarray. Here’s a detailed breakdown of the approach:

  1. Initialization:

    • Let n be the length of nums.
    • Create an array cnt of size 32 to count the number of active bits in the current window.
    • Initialize ans to n + 1 as a placeholder for the minimum length result. This value ensures that if no valid subarray is found, it can be identified by the comparison to n.
    • Use variables s and i to track the current bitwise OR of the subarray and the left pointer, respectively.
  2. Iterating with the Right Pointer:

    • Use a for loop with j iterating over each element x in nums.
    • Update s with the bitwise OR of x.
    • For each bit in x that is set, increment the corresponding count in cnt.
  3. Adjusting the Left Pointer:

    • If s meets or exceeds k, attempt to shrink the window by incrementing i.
    • Inside this inner while loop, update ans to the minimum of its current value and the window size j - i + 1.
    • For the element nums[i] being removed, decrement its corresponding bits in cnt. If a bit count reaches zero, remove that bit from s using the XOR operation.
    • Increment i to continue shrinking the subarray.
  4. Determine the Result:

    • After the loop completes, return -1 if ans is still greater than n, indicating no valid subarray was found. Otherwise, return ans, which represents the shortest length of the special subarray.

The algorithm leverages the properties of bitwise operations to keep the time complexity efficient and uses additional space for counting bits but remains optimal for practical input sizes.

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 use an example to illustrate the solution approach. Suppose nums = [1, 2, 4, 6] and k = 7.

  1. Initialization:

    • Length of nums, n = 4.
    • cnt initialized as an array of size 32, all elements set to 0.
    • Set ans = n + 1 = 5, s = 0, and left pointer i = 0.
  2. Iterating with the Right Pointer:

    • Start with j = 0, nums[j] = 1. Update s with s = s | 1 = 1. Increment bit counts in cnt for bits set in 1.
    • Move to j = 1, nums[j] = 2. Update s with s = s | 2 = 3. Update cnt for bits set in 2.
    • Move to j = 2, nums[j] = 4. Update s with s = s | 4 = 7. Update cnt for bits set in 4.
  3. Adjusting the Left Pointer:

    • Now s = 7, which meets k = 7. Enter the inner loop to try and minimize the subarray:
      • Current window size j - i + 1 = 3. Update ans = min(5, 3) = 3.
      • Update s by removing nums[i] = 1: s = s ^ 1 = 6. Decrement bit counts in cnt, and increment i to 1.
    • Now s = 6 is less than k, exit the inner loop.
  4. Continue with the Right Pointer:

    • Move to j = 3, nums[j] = 6. Update s with s = s | 6 = 6.
    • Since s < 7, do not enter the inner loop.
  5. Determine the Result:

    • The traversal ends. The shortest special subarray length found is ans = 3. Thus, return 3, which is the length of the subarray [2, 4, 6].

In this example, the method successfully identifies and returns the length of the shortest subarray [2, 4, 6] that meets the OR requirement of being at least k = 7.

Solution Implementation

1from typing import List
2
3class Solution:
4    def minimumSubarrayLength(self, nums: List[int], k: int) -> int:
5        n = len(nums)  # Length of the input array
6        bit_count = [0] * 32  # Count of bits set in the current window
7        min_length = n + 1  # Initialize the minimum length to a large value
8        current_or = window_start = 0  # Current OR value and the start of the window
9
10        # Enumerate over each number in the list with its index
11        for window_end, num in enumerate(nums):
12            current_or |= num  # Update the OR value with the current number
13          
14            # Update the bit count for the current number
15            for bit_position in range(32):
16                if num >> bit_position & 1:
17                    bit_count[bit_position] += 1
18          
19            # Adjust the window as long as the OR is greater than or equal to k
20            while current_or >= k and window_start <= window_end:
21                # Update the minimum subarray length found
22                min_length = min(min_length, window_end - window_start + 1)
23                left_num = nums[window_start]  # Number being removed from the window
24
25                # Update the bit count as left_num leaves the window
26                for bit_position in range(32):
27                    if left_num >> bit_position & 1:
28                        bit_count[bit_position] -= 1
29                        # If no numbers in the window have this bit set, adjust the current OR
30                        if bit_count[bit_position] == 0:
31                            current_or ^= 1 << bit_position
32              
33                # Move the start of the window forward
34                window_start += 1
35      
36        # Return the minimum length if a valid subarray was found, otherwise -1
37        return -1 if min_length > n else min_length
38
1class Solution {
2    public int minimumSubarrayLength(int[] nums, int k) {
3        int n = nums.length;
4        int[] bitCounts = new int[32]; // Array to count the occurrences of each bit in the subarray
5        int minLength = n + 1; // Initialize the minimum length to a value greater than possible max length
6
7        // Use two pointers to manage the sliding window
8        for (int left = 0, right = 0, currentOr = 0; right < n; ++right) {
9            currentOr |= nums[right]; // Update the OR value of the current subarray
10
11            // Update the count of each bit in the subarray
12            for (int bitIndex = 0; bitIndex < 32; ++bitIndex) {
13                if ((nums[right] >> bitIndex & 1) == 1) {
14                    ++bitCounts[bitIndex];
15                }
16            }
17
18            // Shrink the left side of the window as much as possible while the OR is still greater than or equal to k
19            while (currentOr >= k && left <= right) {
20                minLength = Math.min(minLength, right - left + 1); // Update the minimum length if needed
21
22                // Decrease the bit counts for the element at the left edge of the window
23                for (int bitIndex = 0; bitIndex < 32; ++bitIndex) {
24                    if ((nums[left] >> bitIndex & 1) == 1) {
25                        if (--bitCounts[bitIndex] == 0) {
26                            currentOr ^= 1 << bitIndex; // Update OR value by removing bits that are no longer in the window
27                        }
28                    }
29                }
30                ++left; // Move the left pointer to the right, reducing the window size
31            }
32        }
33
34        // If minLength remains greater than n, it indicates no valid subarray was found
35        return minLength > n ? -1 : minLength;
36    }
37}
38
1class Solution {
2public:
3    // Function to find the minimum length of a contiguous subarray with a bitwise OR >= k
4    int minimumSubarrayLength(vector<int>& nums, int k) {
5        int n = nums.size();
6        // Initialize a count array for the 32 bits
7        int count[32] = {};
8        int result = n + 1;  // Initialize result to a value greater than any possible subarray length
9
10        // Two pointers for the sliding window (i for the start, j for the end)
11        for (int i = 0, j = 0, current_or = 0; j < n; ++j) {
12            // Update the current OR for the sliding window by including the new element
13            current_or |= nums[j];
14
15            // Update the count array for each bit
16            for (int bit = 0; bit < 32; ++bit) {
17                if ((nums[j] >> bit) & 1) {
18                    ++count[bit];
19                }
20            }
21
22            // Shrink the window from the left side while the current OR is >= k
23            for (; current_or >= k && i <= j; ++i) {
24                // Calculate the current subarray length and update the result if it's the smallest found
25                result = min(result, j - i + 1);
26
27                // Update the count array for the bits being removed and adjust current OR
28                for (int bit = 0; bit < 32; ++bit) {
29                    if ((nums[i] >> bit) & 1) {
30                        if (--count[bit] == 0) {
31                            current_or ^= 1 << bit;  // Toggle off the bit in current OR if no more numbers in window set that bit
32                        }
33                    }
34                }
35            }
36        }
37
38        // If result is still greater than n, no valid subarray was found
39        return result > n ? -1 : result;
40    }
41};
42
1// Function to find the minimum length of a subarray such that the bitwise OR of the subarray is at least k.
2function minimumSubarrayLength(nums: number[], k: number): number {
3    const n = nums.length; // Get the length of the given array.
4    let ans = n + 1; // Initialize the answer with a value greater than the array length to later find the minimum.
5    const cnt: number[] = new Array<number>(32).fill(0); // Initialize a count array to track bit occurrences up to 31 bits.
6
7    // Initialize two pointers i and j, and a variable s for bitwise OR sum.
8    for (let i = 0, j = 0, s = 0; j < n; ++j) {
9        s |= nums[j]; // Update s with bitwise OR including nums[j].
10
11        // Update the count of bits set to 1 for the current element.
12        for (let h = 0; h < 32; ++h) {
13            if (((nums[j] >> h) & 1) === 1) {
14                ++cnt[h];
15            }
16        }
17
18        // Shrink the window from the left to find the minimum subarray length with OR >= k.
19        for (; s >= k && i <= j; ++i) {
20            ans = Math.min(ans, j - i + 1); // Update the answer if a smaller subarray is found.
21
22            // Update count for the leftmost element of the window and adjust s accordingly.
23            for (let h = 0; h < 32; ++h) {
24                if (((nums[i] >> h) & 1) === 1 && --cnt[h] === 0) {
25                    s ^= 1 << h; // Remove the bit from s if its count is zero after decrement.
26                }
27            }
28        }
29    }
30
31    // Return -1 if no valid subarray is found, otherwise return the minimum length found.
32    return ans === n + 1 ? -1 : ans;
33}
34

Time and Space Complexity

  • Time Complexity: The time complexity is O(n \times 32), where n is the length of the nums array. Each element might need up to 32 operations (bit checks and modifications), corresponding to the number of bits in an integer.

  • Space Complexity: The space complexity is O(32), which comes from the cnt list used to store counts for up to 32 bits.

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

Problem: Given a list of tasks and a list of requirements, compute a sequence of tasks that can be performed, such that we complete every task once while satisfying all the requirements.

Which of the following method should we use to solve this problem?


Recommended Readings

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


Load More