LeetCode 3315 - Construct the Minimum Bitwise Array II
We are given an array nums where every element is a prime number. For each value nums[i], we must find the smallest non-negative integer ans[i] such that: where | denotes the bitwise OR operation. If no such value exists, we must place -1 in the answer array at that position.
Difficulty: 🟡 Medium
Topics: Array, Bit Manipulation
Solution
LeetCode 3315 - Construct the Minimum Bitwise Array II
Problem Understanding
We are given an array nums where every element is a prime number. For each value nums[i], we must find the smallest non-negative integer ans[i] such that:
$$ans[i] ;|; (ans[i] + 1) = nums[i]$$
where | denotes the bitwise OR operation.
If no such value exists, we must place -1 in the answer array at that position.
The important detail is that we are not merely looking for any valid value. Among all values that satisfy the equation, we must choose the minimum possible one.
The input consists of up to 100 prime numbers, and each prime can be as large as $10^9$. The relatively small array size suggests that even an $O(n \log M)$ solution would be easily fast enough, where $M$ is the magnitude of the numbers.
The challenge is understanding the structure of:
$$x ;|; (x+1)$$
and then reversing this operation efficiently.
A few important observations arise immediately.
If nums[i] = 2, there is no solution. The only possible candidates are:
0 | 1 = 11 | 2 = 3
No value produces 2.
Another important observation is that every prime except 2 is odd. Since x | (x+1) is always odd, all odd primes potentially have solutions.
The key task is determining the minimum valid x for a given odd prime.
Approaches
Brute Force
A straightforward approach would be to try every possible value x from 0 upward and check whether:
$$x | (x+1) = nums[i]$$
The first valid value found would be the answer because we are searching in increasing order.
This approach is correct because it exhaustively checks every candidate and eventually finds the minimum valid one if it exists.
However, it is completely impractical. Since nums[i] can be as large as $10^9$, the search space may contain hundreds of millions of candidates.
Key Insight
Consider how adding one changes binary representations.
Suppose x ends with a block of trailing ones:
xxxxx01111
Adding one produces:
xxxxx10000
Now compute the OR:
xxxxx01111
xxxxx10000
-----------
xxxxx11111
The result is obtained by turning the rightmost zero bit of x into 1, while all lower bits remain 1.
Therefore, if:
$$y = x | (x+1)$$
then y must contain a contiguous suffix of ones.
To reverse the process, let y = nums[i].
We want the smallest possible x.
Since:
$$y = x | (x+1)$$
the rightmost zero involved in the transition becomes a one in y.
To minimize x, we want to clear the highest possible bit that can be removed while still allowing the OR operation to reconstruct y.
For an odd number y, find the first zero bit when scanning from the least significant bit upward through the trailing ones. If that zero is at position p, then the bit immediately below it, position p-1, can be cleared to obtain the minimum valid x.
Equivalently:
- Find the first zero bit in
y. - Let its position be
p. - Answer is:
$$y - 2^{p-1}$$
The only exception is y = 2, which has no solution.
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n·M) | O(1) | Tries every candidate value |
| Optimal | O(n·log M) | O(1) | Uses binary structure of x OR (x+1) |
Algorithm Walkthrough
- Create an empty answer array.
- Process each prime number
numindependently. - If
num == 2, append-1because no integerxsatisfiesx | (x + 1) = 2. - Otherwise, scan bits from least significant to most significant until finding the first bit position whose value is
0. - Suppose that zero bit is found at position
p. - The minimum valid value is obtained by clearing bit
p - 1fromnum. - Compute:
$$ans = num - 2^{p-1}$$ 8. Append this value to the result array. 9. After processing all elements, return the result.
Why it works
For an odd number, the bits from position 0 through p-1 are all 1, and bit p is the first 0. Any value x satisfying the equation must contain a zero somewhere inside that suffix of ones so that adding one creates the carry chain. To minimize x, we clear the highest bit that can participate in this carry chain, namely bit p-1. Then x+1 restores that bit and propagates appropriately, causing the OR result to reconstruct the original number. Any smaller modification would fail to produce the target, and any larger modification would yield a larger x.
Python Solution
from typing import List
class Solution:
def minBitwiseArray(self, nums: List[int]) -> List[int]:
result = []
for num in nums:
if num == 2:
result.append(-1)
continue
pos = 0
while (num >> pos) & 1:
pos += 1
result.append(num - (1 << (pos - 1)))
return result
The implementation processes each number independently.
When the number equals 2, we immediately append -1 because no valid solution exists.
For every other prime, which is necessarily odd, we search for the first zero bit. The loop advances while the current bit equals 1. Once the first zero bit is found, its position is stored in pos.
If the first zero occurs at position pos, then the minimum valid answer is obtained by subtracting 2^(pos-1) from the original number. This effectively clears the bit immediately below the first zero.
The resulting value is appended to the answer array.
Go Solution
func minBitwiseArray(nums []int) []int {
result := make([]int, 0, len(nums))
for _, num := range nums {
if num == 2 {
result = append(result, -1)
continue
}
pos := 0
for ((num >> pos) & 1) == 1 {
pos++
}
result = append(result, num-(1<<(pos-1)))
}
return result
}
The Go implementation follows exactly the same logic as the Python version.
Go uses integer bit shifting in the same way. Since all values are at most $10^9$, there are no overflow concerns when computing powers of two involved in the solution. The result slice is preallocated with capacity len(nums) to avoid unnecessary reallocations.
Worked Examples
Example 1
Input:
nums = [2,3,5,7]
Number = 2
No solution exists.
| num | answer |
|---|---|
| 2 | -1 |
Number = 3
Binary:
3 = 11₂
Scanning bits:
| Position | Bit |
|---|---|
| 0 | 1 |
| 1 | 1 |
| 2 | 0 |
First zero bit is at position 2.
ans = 3 - 2^(2-1)
= 3 - 2
= 1
Verification:
1 OR 2 = 3
Number = 5
Binary:
5 = 101₂
Scanning bits:
| Position | Bit |
|---|---|
| 0 | 1 |
| 1 | 0 |
First zero bit is at position 1.
ans = 5 - 1
= 4
Verification:
4 OR 5 = 5
Number = 7
Binary:
7 = 111₂
Scanning bits:
| Position | Bit |
|---|---|
| 0 | 1 |
| 1 | 1 |
| 2 | 1 |
| 3 | 0 |
First zero bit is at position 3.
ans = 7 - 4
= 3
Verification:
3 OR 4 = 7
Final answer:
[-1,1,4,3]
Example 2
Input:
nums = [11,13,31]
Number = 11
11 = 1011₂
First zero bit is at position 2.
ans = 11 - 2
= 9
Check:
9 OR 10 = 11
Number = 13
13 = 1101₂
First zero bit is at position 1.
ans = 13 - 1
= 12
Check:
12 OR 13 = 13
Number = 31
31 = 11111₂
First zero bit is at position 5.
ans = 31 - 16
= 15
Check:
15 OR 16 = 31
Final answer:
[9,12,15]
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n log M) | At most 31 bit positions are scanned for each number |
| Space | O(1) | Excluding the output array |
Since nums[i] ≤ 10^9, every number contains at most 30 significant bits. For each element we scan upward until reaching the first zero bit, so the work per element is bounded by a small constant. Therefore the overall complexity is effectively linear in the size of the input array.
Test Cases
from typing import List
s = Solution()
assert s.minBitwiseArray([2, 3, 5, 7]) == [-1, 1, 4, 3] # provided example 1
assert s.minBitwiseArray([11, 13, 31]) == [9, 12, 15] # provided example 2
assert s.minBitwiseArray([2]) == [-1] # smallest prime
assert s.minBitwiseArray([3]) == [1] # smallest valid odd prime
assert s.minBitwiseArray([5]) == [4] # single trailing one
assert s.minBitwiseArray([7]) == [3] # all ones
assert s.minBitwiseArray([17]) == [16] # binary 10001
assert s.minBitwiseArray([19]) == [17] # binary 10011
assert s.minBitwiseArray([127]) == [63] # long run of trailing ones
assert s.minBitwiseArray([3, 5, 11, 13, 31]) == [1, 4, 9, 12, 15] # mixed inputs
Test Summary
| Test | Why |
|---|---|
[2] |
Verifies the only impossible prime |
[3] |
Smallest valid odd prime |
[5] |
First zero bit appears immediately |
[7] |
Number consisting entirely of ones |
[17] |
Sparse binary representation |
[19] |
Multiple trailing ones |
[127] |
Long carry chain |
| Mixed array | Verifies independent processing |
| Example 1 | Matches official example |
| Example 2 | Matches official example |
Edge Cases
Prime Number 2
The value 2 is special because it is the only even prime. The expression x | (x + 1) is always odd, since one of the two consecutive numbers must have its least significant bit set. Therefore producing 2 is impossible. The implementation explicitly checks for this case and returns -1.
Numbers Consisting Entirely of Ones
Values such as 7, 31, and 127 have binary forms like 111, 11111, and 1111111. A common mistake is assuming there is always a zero bit inside the representation. The algorithm continues scanning beyond the highest set bit until it encounters the first zero bit, which naturally exists at the next position. This correctly produces answers such as 3, 15, and 63.
First Zero Bit Immediately Above the Least Significant Bit
Numbers like 5 (101₂) and 13 (1101₂) have their first zero bit very close to the right side. It is easy to make an off by one error when computing which bit to clear. The implementation carefully subtracts 1 << (pos - 1), where pos is the first zero bit position, ensuring the correct bit is removed.
Large Prime Values Near the Constraint Limit
Primes may be as large as $10^9$. A brute force search would be infeasible. The bit manipulation solution only examines a small number of bits, at most around 31, so performance remains excellent even for the largest allowed inputs.