3315. Construct the Minimum Bitwise Array II
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
wherea OR (a + 1)
equals the corresponding element innums
- The bitwise OR operation between consecutive integers
a
anda + 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 innums
have no valid answer - Among prime numbers, only 2 is even, so when
nums[i] = 2
, the answer is-1
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
), thena + 1 = 6
(binary:110
) - If
a = 7
(binary:111
), thena + 1 = 8
(binary:1000
)
The key insight is that a
and a + 1
differ in a specific pattern: all trailing 1
s in a
become 0
s in a + 1
, and the first 0
bit becomes 1
. When we OR these two numbers, we get all 1
s 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 1
s 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 byi
positions& 1
isolates the bit at that position^ 1
flips the bit (XOR with 1), so if the bit is0
, we get1
(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 single1
bit at position(i-1)
x ^ mask
flips that specific bit inx
Example walkthrough:
Let's say nums[i] = 13
(binary: 1101
)
- Check if it's
2
- No, continue - Find first
0
bit:- Position 1:
1101 >> 1 = 110
, bit is0
, found it!
- Position 1:
- Calculate answer:
1101 ^ (1 << 0) = 1101 ^ 0001 = 1100 = 12
- 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 EvaluatorExample 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 is1
(not 0) - Position 2:
0111 >> 2 = 01
, last bit is1
(not 0) - Position 3:
0111 >> 3 = 0
, last bit is0
(found it!)
- Position 1:
- 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 is0
(found it!)
- Position 1:
- 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
In a binary min heap, the minimum element can be found in:
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!