Facebook Pixel

3315. Construct the Minimum Bitwise Array II

MediumBit ManipulationArray
Leetcode Link

Problem Description

You are given an array nums containing n prime numbers. Your task is to construct a new array ans of the same length n where each element satisfies a specific bitwise condition.

For each index i, you need to find a value for ans[i] such that when you perform a bitwise OR operation between ans[i] and ans[i] + 1, the result equals nums[i]. In other words: ans[i] OR (ans[i] + 1) == nums[i].

Additionally, among all possible values that satisfy this condition, you must choose the minimum value for each ans[i].

If no such value exists that satisfies the condition for a particular index, set ans[i] = -1.

Key Points:

  • The input array contains only prime numbers
  • For each element, you need to find the smallest number a where a OR (a + 1) equals the corresponding element in nums
  • The bitwise OR operation between consecutive integers a and a + 1 has special properties that can be exploited
  • Since a OR (a + 1) always produces an odd result (because the least significant bit will always be 1), even numbers in nums have no valid answer
  • Among prime numbers, only 2 is even, so when nums[i] = 2, the answer is -1
Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

Let's think about what happens when we perform a OR (a + 1) for any integer a.

When we add 1 to a number a, we're essentially flipping bits from right to left until we hit the first 0 bit, which becomes 1. For example:

  • If a = 5 (binary: 101), then a + 1 = 6 (binary: 110)
  • If a = 7 (binary: 111), then a + 1 = 8 (binary: 1000)

The key insight is that a and a + 1 differ in a specific pattern: all trailing 1s in a become 0s in a + 1, and the first 0 bit becomes 1. When we OR these two numbers, we get all 1s up to and including that position where the first 0 was.

This means a OR (a + 1) always results in an odd number (the least significant bit is always 1). Therefore, if nums[i] is even, there's no solution. Since we're dealing with prime numbers, only 2 is even, so we return -1 for it.

For odd numbers, we need to work backwards. If nums[i] = a OR (a + 1), then nums[i] has a specific pattern: it has consecutive 1s from the right, possibly followed by more bits. The transition from a to a + 1 fills in all bits up to the rightmost 0 in a.

To find a from nums[i], we need to identify which bit in a was the rightmost 0 that got flipped to create this OR result. Looking at nums[i], we find the first 0 bit from the right (starting from bit position 1, not 0). This tells us where the transition happened. The bit just before this position in the result came from the carry operation when adding 1 to a.

Therefore, to reconstruct a, we take nums[i] and flip the bit at position (i-1) where i is the position of the first 0 bit in nums[i]. This can be done using XOR: ans[i] = nums[i] XOR (1 << (i-1)).

Solution Approach

The implementation follows the bit manipulation strategy we identified:

Step 1: Handle the special case For each number x in nums, first check if it equals 2. Since 2 is even and a OR (a + 1) always produces odd results, there's no valid answer, so we append -1.

Step 2: Find the rightmost 0 bit For odd numbers (all other primes), we iterate through bit positions from 1 to 31 (sufficient for 32-bit integers). We check each bit position i using the expression x >> i & 1 ^ 1:

  • x >> i shifts the number right by i positions
  • & 1 isolates the bit at that position
  • ^ 1 flips the bit (XOR with 1), so if the bit is 0, we get 1 (true)

This effectively finds the first 0 bit when scanning from right to left.

Step 3: Calculate the answer Once we find the first 0 bit at position i, we need to flip the bit at position (i-1) in the original number. This is done using XOR operation:

ans.append(x ^ (1 << (i - 1)))
  • 1 << (i - 1) creates a mask with a single 1 bit at position (i-1)
  • x ^ mask flips that specific bit in x

Example walkthrough: Let's say nums[i] = 13 (binary: 1101)

  1. Check if it's 2 - No, continue
  2. Find first 0 bit:
    • Position 1: 1101 >> 1 = 110, bit is 0, found it!
  3. Calculate answer: 1101 ^ (1 << 0) = 1101 ^ 0001 = 1100 = 12
  4. Verify: 12 OR 13 = 1100 OR 1101 = 1101 = 13

The algorithm processes each element independently in O(log n) time for bit operations, resulting in overall O(n × log m) complexity where n is the array length and m is the maximum value.

Ready to land your dream job?

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

Start Evaluator

Example Walkthrough

Let's walk through the solution with a small example: nums = [7, 2, 5]

Processing nums[0] = 7:

  • Binary representation: 7 = 0111
  • Check if it's 2: No, so we proceed
  • Find the rightmost 0 bit (starting from position 1):
    • Position 1: 0111 >> 1 = 011, last bit is 1 (not 0)
    • Position 2: 0111 >> 2 = 01, last bit is 1 (not 0)
    • Position 3: 0111 >> 3 = 0, last bit is 0 (found it!)
  • The first 0 bit is at position 3
  • Calculate ans[0]: 7 XOR (1 << 2) = 0111 XOR 0100 = 0011 = 3
  • Verify: 3 OR 4 = 0011 OR 0100 = 0111 = 7

Processing nums[1] = 2:

  • Check if it's 2: Yes!
  • Since 2 is even and a OR (a+1) always produces odd numbers, ans[1] = -1

Processing nums[2] = 5:

  • Binary representation: 5 = 0101
  • Check if it's 2: No, so we proceed
  • Find the rightmost 0 bit:
    • Position 1: 0101 >> 1 = 010, last bit is 0 (found it!)
  • The first 0 bit is at position 1
  • Calculate ans[2]: 5 XOR (1 << 0) = 0101 XOR 0001 = 0100 = 4
  • Verify: 4 OR 5 = 0100 OR 0101 = 0101 = 5

Final answer: ans = [3, -1, 4]

Let's verify each result:

  • ans[0] = 3: 3 OR 4 = 0011 OR 0100 = 0111 = 7
  • ans[1] = -1: No valid value exists for nums[1] = 2
  • ans[2] = 4: 4 OR 5 = 0100 OR 0101 = 0101 = 5

Solution Implementation

1class Solution:
2    def minBitwiseArray(self, nums: List[int]) -> List[int]:
3        """
4        For each number in nums, find the minimum value y such that y | (y + 1) = num.
5        If no such value exists, return -1 for that position.
6      
7        Args:
8            nums: List of integers to process
9          
10        Returns:
11            List of minimum values or -1 if no valid value exists
12        """
13        result = []
14      
15        for num in nums:
16            # Special case: when num is 2, no valid y exists
17            # Because if y | (y + 1) = 2 (binary: 10), there's no valid y
18            if num == 2:
19                result.append(-1)
20            else:
21                # Find the rightmost 0 bit in num's binary representation
22                # Starting from bit position 1 (2^1 = 2)
23                for bit_position in range(1, 32):
24                    # Check if the bit at current position is 0
25                    # (x >> i) & 1 gives the bit at position i
26                    # ^ 1 flips it, so we're checking if bit is 0
27                    if ((num >> bit_position) & 1) ^ 1:
28                        # Found the rightmost 0 bit at position bit_position
29                        # Flip the bit at position (bit_position - 1) to get the answer
30                        # This gives us the minimum y where y | (y + 1) = num
31                        result.append(num ^ (1 << (bit_position - 1)))
32                        break
33      
34        return result
35
1class Solution {
2    public int[] minBitwiseArray(List<Integer> nums) {
3        int n = nums.size();
4        int[] result = new int[n];
5      
6        // Process each number in the input list
7        for (int i = 0; i < n; i++) {
8            int currentNum = nums.get(i);
9          
10            // Special case: if the number is 2, no valid answer exists
11            if (currentNum == 2) {
12                result[i] = -1;
13            } else {
14                // Find the first 0 bit from left to right (starting from bit position 1)
15                for (int bitPosition = 1; bitPosition < 32; bitPosition++) {
16                    // Check if the bit at current position is 0
17                    if ((currentNum >> bitPosition & 1) == 0) {
18                        // XOR with 2^(bitPosition-1) to flip the bit at position (bitPosition-1)
19                        // This gives us the minimum value where value | (value + 1) equals currentNum
20                        result[i] = currentNum ^ (1 << (bitPosition - 1));
21                        break;
22                    }
23                }
24            }
25        }
26      
27        return result;
28    }
29}
30
1class Solution {
2public:
3    vector<int> minBitwiseArray(vector<int>& nums) {
4        vector<int> result;
5      
6        for (int num : nums) {
7            // Special case: when num is 2, no valid answer exists
8            // This is because 2 in binary is 10, and we need ans | (ans + 1) = 2
9            // No such ans exists that satisfies this condition
10            if (num == 2) {
11                result.push_back(-1);
12            } else {
13                // Find the minimum value ans such that ans | (ans + 1) = num
14                // Strategy: Find the first 0 bit starting from position 1
15                // Then flip the bit at position (i-1)
16                for (int bitPos = 1; bitPos < 32; ++bitPos) {
17                    // Check if the bit at position bitPos is 0
18                    // Using XOR with 1 to check if bit is 0 (0 ^ 1 = 1, 1 ^ 1 = 0)
19                    if (((num >> bitPos) & 1) ^ 1) {
20                        // Found first 0 bit at position bitPos
21                        // Flip the bit at position (bitPos - 1) to get the answer
22                        result.push_back(num ^ (1 << (bitPos - 1)));
23                        break;
24                    }
25                }
26            }
27        }
28      
29        return result;
30    }
31};
32
1/**
2 * Finds the minimum bitwise array for given numbers
3 * For each number x in nums, finds the smallest number a such that a | (a + 1) = x
4 * Returns -1 if no such number exists
5 */
6function minBitwiseArray(nums: number[]): number[] {
7    const result: number[] = [];
8  
9    for (const currentNumber of nums) {
10        // Special case: 2 has no valid answer
11        // Since 2 in binary is 10, there's no a where a | (a + 1) = 2
12        if (currentNumber === 2) {
13            result.push(-1);
14        } else {
15            // Find the position of the first 0 bit from left to right
16            // This helps us determine the minimum value that satisfies a | (a + 1) = x
17            for (let bitPosition = 1; bitPosition < 32; ++bitPosition) {
18                // Check if the bit at current position is 0
19                if (((currentNumber >> bitPosition) & 1) ^ 1) {
20                    // Found the first 0 bit from the right after position 0
21                    // The answer is x with the bit at (bitPosition - 1) flipped to 0
22                    result.push(currentNumber ^ (1 << (bitPosition - 1)));
23                    break;
24                }
25            }
26        }
27    }
28  
29    return result;
30}
31

Time and Space Complexity

Time Complexity: O(n × log M)

The algorithm iterates through each element in the nums array (n elements). For each element x, it performs a bit-checking operation that iterates through bit positions from 1 to 31 in the worst case. Since we're checking up to 32 bit positions (which corresponds to log M where M is the maximum value that can be represented), the inner loop runs at most O(log M) times. Therefore, the overall time complexity is O(n × log M), where n is the length of the array and M is the maximum value in the array.

Space Complexity: O(1) (excluding the output array)

The algorithm uses only a constant amount of extra space for variables like i and x in the loops. The ans array is used to store the results, but as mentioned in the reference, we ignore the space consumption of the answer array when analyzing space complexity. Therefore, the space complexity is O(1).

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

Common Pitfalls

1. Incorrect Bit Position Calculation

A common mistake is confusing the bit position where we find the first 0 with the bit position we need to flip. When we find the first 0 bit at position i, we need to flip the bit at position (i-1), not at position i itself.

Incorrect approach:

if ((num >> bit_position) & 1) ^ 1:
    result.append(num ^ (1 << bit_position))  # Wrong! Flipping at position i
    break

Correct approach:

if ((num >> bit_position) & 1) ^ 1:
    result.append(num ^ (1 << (bit_position - 1)))  # Correct! Flipping at position (i-1)
    break

2. Mishandling Even Numbers

The problem states that the input contains only prime numbers, but if the logic were extended to handle general numbers, a pitfall would be checking only for 2 instead of all even numbers. Since a | (a+1) always produces odd results, ALL even numbers should return -1.

Limited approach:

if num == 2:
    result.append(-1)

More robust approach for general case:

if num % 2 == 0:  # or use: if not num & 1:
    result.append(-1)

3. Starting Bit Search from Wrong Position

Another pitfall is starting the bit position search from 0 instead of 1. If we start from position 0, we'd be looking at the least significant bit, which for odd numbers is always 1. This would cause us to skip checking important positions.

Incorrect:

for bit_position in range(0, 32):  # Starting from 0
    if ((num >> bit_position) & 1) ^ 1:
        # This would try to flip bit at position -1 for the first iteration!
        result.append(num ^ (1 << (bit_position - 1)))

Correct:

for bit_position in range(1, 32):  # Starting from 1
    if ((num >> bit_position) & 1) ^ 1:
        result.append(num ^ (1 << (bit_position - 1)))

4. Not Breaking After Finding the Answer

Forgetting to break after finding the first 0 bit would cause the loop to continue and potentially overwrite the correct answer with an incorrect one from a higher bit position.

Missing break:

for bit_position in range(1, 32):
    if ((num >> bit_position) & 1) ^ 1:
        result.append(num ^ (1 << (bit_position - 1)))
        # Missing break! Would continue and possibly append multiple values

5. Insufficient Bit Range

While 32 bits is typically sufficient for standard integers, if dealing with larger numbers, the range might need adjustment. Not considering the actual size of input numbers could lead to missing valid solutions for very large primes.

Solution: Dynamically determine the bit length:

max_bits = num.bit_length() + 1  # Add 1 to ensure we check beyond the highest set bit
for bit_position in range(1, max_bits):
    # ... rest of the logic
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

In a binary min heap, the minimum element can be found in:


Recommended Readings

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

Load More