3125. Maximum Number That Makes Result of Bitwise AND Zero π
Problem Description
Given an integer n
, you need to find the maximum possible integer x
that satisfies two conditions:
x
must be less than or equal ton
(x <= n
)- The bitwise AND of all integers in the range
[x, n]
must equal 0
In other words, when you perform bitwise AND operation on all numbers from x
to n
inclusive (i.e., x & (x+1) & (x+2) & ... & n
), the result should be 0.
For example, if n = 7
, you need to find the largest x
such that the AND of all numbers from x
to 7 equals 0. The solution works by recognizing that for the AND of consecutive numbers to be 0, at least one bit position must have both 0 and 1 across the range. The key insight is that x
and x+1
will always differ in at least the least significant bit, and finding the right x
means it should be of the form 2^k - 1
(all 1s in binary) where x+1
would be 2^k
(a single 1 followed by all 0s). The maximum such x
that doesn't exceed n
would be 2^(highest_bit_position) - 1
, where the highest bit position is determined by the position of the most significant 1 in n
's binary representation.
Intuition
To understand why the bitwise AND of a range becomes 0, let's think about what happens when we AND consecutive numbers. When we AND two or more numbers, a bit position in the result is 1 only if that bit is 1 in ALL the numbers.
The key observation is that consecutive numbers have a special property: x
and x+1
will always differ in at least the least significant bit. For example:
5
in binary is101
6
in binary is110
- Their AND is
100
But we want the AND to be exactly 0, not just smaller. This means we need numbers in our range where every bit position has at least one 0 among all the numbers.
The most efficient way to guarantee this is to include a pair of numbers like 01111...
and 10000...
in binary. These are numbers of the form 2^k - 1
and 2^k
. When we AND these two:
01111
(which is2^4 - 1 = 15
)10000
(which is2^4 = 16
)- Result:
00000
(which is 0)
Since we want the maximum x
, we should choose the largest such pair that fits within our constraint x <= n
. Looking at n
's binary representation, if n
has its highest bit at position k
, then the largest valid x
would be 2^(k-1) - 1
. This ensures that x+1 = 2^(k-1)
is still less than or equal to n
, and the AND of at least these two numbers (and therefore the entire range) will be 0.
For example, if n = 10
(binary 1010
), the highest bit is at position 3 (counting from 0). So x = 2^3 - 1 = 7
(binary 0111
), and the range [7, 10]
includes both 0111
and 1000
, guaranteeing their AND is 0.
Solution Approach
The solution uses bit manipulation to efficiently find the answer in constant time. Here's how the implementation works:
The key insight from the reference approach is that we need to find the highest bit position in n
's binary representation and construct our answer based on that.
Step 1: Find the highest bit position
We use n.bit_length()
which returns the number of bits needed to represent n
. For example:
- If
n = 10
(binary1010
),bit_length()
returns 4 - If
n = 7
(binary111
),bit_length()
returns 3
Step 2: Calculate the maximum x
The formula (1 << (n.bit_length() - 1)) - 1
does the following:
n.bit_length() - 1
gives us the position of the second-highest possible bit1 << (n.bit_length() - 1)
creates a number with only that bit set (equivalent to2^(bit_length - 1)
)- Subtracting 1 from this power of 2 gives us a number with all bits set up to that position
For example, if n = 10
:
n.bit_length()
= 41 << (4 - 1)
=1 << 3
= 8 (binary1000
)8 - 1
= 7 (binary0111
)
This ensures that:
- Our answer
x = 7
is less thann = 10
- The range
[7, 10]
includes both0111
(7) and1000
(8) - Since
7
and8
are consecutive numbers of the form2^k - 1
and2^k
, their AND is 0 - Therefore, the AND of the entire range
[7, 10]
is also 0
The time complexity is O(1)
since we're only performing bit operations, and the space complexity is also O(1)
as we use only a constant amount of extra space.
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 n = 13
to illustrate the approach.
Step 1: Analyze n in binary
n = 13
in binary is1101
- The highest bit is at position 3 (counting from 0)
Step 2: Find the bit length
n.bit_length()
returns 4 (we need 4 bits to represent 13)
Step 3: Calculate x using the formula
- Formula:
(1 << (n.bit_length() - 1)) - 1
- Substitute:
(1 << (4 - 1)) - 1
- Simplify:
(1 << 3) - 1
- Calculate:
8 - 1 = 7
- So
x = 7
(binary0111
)
Step 4: Verify the solution Let's check that the AND of all numbers from 7 to 13 equals 0:
- 7:
0111
- 8:
1000
- 9:
1001
- 10:
1010
- 11:
1011
- 12:
1100
- 13:
1101
Performing AND operation step by step:
0111 & 1000 = 0000
(Already 0!)- Since we already have 0, ANDing with any other number will still give 0
- Final result:
0000
β
Why this works: The key is that we found a pair of consecutive numbers (7 and 8) where:
- 7 =
0111
(all 1s in the lower bits, which is2^3 - 1
) - 8 =
1000
(a single 1 in a higher bit, which is2^3
)
These two numbers have no overlapping 1 bits, so their AND is 0. Since the AND includes these two numbers, the entire range's AND must also be 0. The value 7 is the maximum such x
that satisfies our conditions for n = 13
.
Solution Implementation
1class Solution:
2 def maxNumber(self, n: int) -> int:
3 """
4 Returns the maximum number that can be represented using one fewer bit than n.
5
6 This finds the largest number with all bits set to 1 that has fewer bits than n.
7 For example: if n = 10 (binary: 1010, 4 bits), returns 7 (binary: 111, 3 bits).
8
9 Args:
10 n: A positive integer
11
12 Returns:
13 The maximum number with (bit_length(n) - 1) bits, all set to 1
14 """
15 # Get the number of bits required to represent n
16 num_bits = n.bit_length()
17
18 # Calculate 2^(num_bits - 1) - 1
19 # This creates a number with all bits set to 1 using one fewer bit than n
20 max_with_fewer_bits = (1 << (num_bits - 1)) - 1
21
22 return max_with_fewer_bits
23
1class Solution {
2 /**
3 * Finds the maximum number that can be represented using the same number of bits as n.
4 * This is achieved by finding the highest bit position in n and creating a number
5 * with all bits set to 1 up to that position.
6 *
7 * For example:
8 * - If n = 5 (binary: 101), highest bit position is 2, result is 7 (binary: 111)
9 * - If n = 8 (binary: 1000), highest bit position is 3, result is 15 (binary: 1111)
10 *
11 * @param n The input number
12 * @return The maximum number with the same bit length as n
13 */
14 public long maxNumber(long n) {
15 // Calculate the position of the highest set bit in n
16 // Long.numberOfLeadingZeros(n) returns the number of zero bits preceding the highest-order one bit
17 // 63 - Long.numberOfLeadingZeros(n) gives us the position of the highest set bit (0-indexed)
18 int highestBitPosition = 63 - Long.numberOfLeadingZeros(n);
19
20 // Create a number with all bits set to 1 up to and including the highest bit position
21 // 1L << (highestBitPosition + 1) creates a number with a single 1 bit at position (highestBitPosition + 1)
22 // Subtracting 1 from this gives us all 1s up to position highestBitPosition
23 long maxNumberWithSameBitLength = (1L << (highestBitPosition + 1)) - 1;
24
25 return maxNumberWithSameBitLength;
26 }
27}
28
1class Solution {
2public:
3 long long maxNumber(long long n) {
4 // Find the position of the most significant bit (MSB) in n
5 // __builtin_clzll counts leading zeros in a 64-bit number
6 // For a 64-bit number, if there are k leading zeros, the MSB is at position (63 - k)
7 int msb_position = 63 - __builtin_clzll(n);
8
9 // Create a number with all bits set to 1 up to the MSB position
10 // (1LL << msb_position) creates a number with only the bit at msb_position set to 1
11 // Subtracting 1 from this gives us all bits set to 1 from position 0 to (msb_position - 1)
12 // This represents the largest number that has the same number of bits as n
13 long long result = (1LL << msb_position) - 1;
14
15 return result;
16 }
17};
18
1function maxNumber(n: bigint): bigint {
2 // Find the position of the most significant bit (MSB) in n
3 // We need to manually count the number of bits since TypeScript doesn't have __builtin_clzll
4 // Start from the highest possible bit position and work down
5 let msb_position = 0n;
6 let temp = n;
7
8 // Count the position of the highest set bit
9 while (temp > 0n) {
10 msb_position++;
11 temp >>= 1n;
12 }
13
14 // If n is 0, return 0 (edge case)
15 if (msb_position === 0n) {
16 return 0n;
17 }
18
19 // Create a number with all bits set to 1 up to the MSB position
20 // (1n << msb_position) creates a number with only the bit at msb_position set to 1
21 // Subtracting 1 from this gives us all bits set to 1 from position 0 to (msb_position - 1)
22 // This represents the largest number that has the same number of bits as n
23 const result = (1n << msb_position) - 1n;
24
25 return result;
26}
27
Time and Space Complexity
The time complexity is O(log n)
, and the space complexity is O(1)
.
The time complexity is O(log n)
because the bit_length()
method needs to determine the number of bits required to represent the integer n
, which involves finding the position of the most significant bit. This operation takes logarithmic time relative to the value of n
.
The space complexity is O(1)
because the algorithm only uses a constant amount of extra space regardless of the input size. It performs a few arithmetic operations (bit shift and subtraction) that don't require additional memory allocation proportional to the input.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Misunderstanding the Problem Requirements
A common pitfall is misinterpreting what the problem is actually asking for. Developers might think they need to:
- Find any
x
where the AND operation equals 0, rather than the maximum suchx
- Check every possible value from 1 to
n
by actually computing the AND of ranges - Return the result of the AND operation itself rather than the value
x
Solution: Carefully read that we need the maximum x
value, not the AND result, and understand that we're looking for a pattern rather than brute-forcing.
2. Edge Case: When n = 0
The current implementation doesn't handle n = 0
properly. When n = 0
, bit_length()
returns 0, which would lead to:
1 << (0 - 1)
=1 << -1
, which is undefined behavior in some contexts- In Python, this would result in
0.5
, and0.5 - 1 = -0.5
, which isn't valid
Solution: Add a special check for edge cases:
def maxNumber(self, n: int) -> int:
if n == 0:
return 0
num_bits = n.bit_length()
max_with_fewer_bits = (1 << (num_bits - 1)) - 1
return max_with_fewer_bits
3. Incorrect Bit Manipulation Formula
Developers might incorrectly calculate the result using:
(1 << n.bit_length()) - 1
instead of(1 << (n.bit_length() - 1)) - 1
2 ** (n.bit_length() - 1) - 1
which works but is less efficientn >> 1
or other shifting operations that don't capture the pattern
Solution: Remember that we need exactly one fewer bit than n
, so the formula must be (1 << (bit_length - 1)) - 1
.
4. Not Verifying the AND Condition
Some might return the calculated value without understanding why it guarantees the AND of the range equals 0. This could lead to confidence issues or incorrect modifications.
Solution: Understand the mathematical proof: any range [2^k - 1, n]
where n >= 2^k
will include both 2^k - 1
(all 1s in k bits) and 2^k
(1 followed by k zeros), which AND to 0.
5. Overflow Concerns in Other Languages
While Python handles arbitrary precision integers, in languages like C++ or Java, bit operations on large values could cause overflow.
Solution: For other languages, ensure the input constraints are checked and use appropriate data types (e.g., long long
in C++ or long
in Java) when dealing with large values of n
.
Which of the following is a good use case for backtracking?
Recommended Readings
Greedy Introduction div class responsive iframe iframe src https www youtube com embed WTslqPbj7I title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture web share allowfullscreen iframe div When do we use greedy Greedy algorithms tend to solve optimization problems Typically they will ask you to calculate the max min of some value Commonly you may see this phrased in the problem as max min longest shortest largest smallest etc These keywords can be identified by just scanning
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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
Want a Structured Path to Master System Design Too? Donβt Miss This!