Facebook Pixel

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:

  1. x must be less than or equal to n (x <= n)
  2. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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 is 101
  • 6 in binary is 110
  • 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 is 2^4 - 1 = 15)
  • 10000 (which is 2^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.

Learn more about Greedy and Sorting patterns.

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 (binary 1010), bit_length() returns 4
  • If n = 7 (binary 111), 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 bit
  • 1 << (n.bit_length() - 1) creates a number with only that bit set (equivalent to 2^(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() = 4
  • 1 << (4 - 1) = 1 << 3 = 8 (binary 1000)
  • 8 - 1 = 7 (binary 0111)

This ensures that:

  1. Our answer x = 7 is less than n = 10
  2. The range [7, 10] includes both 0111 (7) and 1000 (8)
  3. Since 7 and 8 are consecutive numbers of the form 2^k - 1 and 2^k, their AND is 0
  4. 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 Evaluator

Example Walkthrough

Let's walk through the solution with n = 13 to illustrate the approach.

Step 1: Analyze n in binary

  • n = 13 in binary is 1101
  • 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 (binary 0111)

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 is 2^3 - 1)
  • 8 = 1000 (a single 1 in a higher bit, which is 2^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 such x
  • 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, and 0.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 efficient
  • n >> 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.

Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a good use case for backtracking?


Recommended Readings

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

Load More