Facebook Pixel

3133. Minimum Array End

MediumBit Manipulation
Leetcode Link

Problem Description

You need to construct an array of positive integers with specific properties:

  1. The array has size n
  2. Each element must be strictly increasing (i.e., nums[i+1] > nums[i] for all valid indices)
  3. The bitwise AND of all elements in the array must equal x
  4. All elements must be positive integers

Your task is to find the minimum possible value of the last element (nums[n-1]) in such an array.

For example, if n = 3 and x = 4, you need to create an array of 3 positive integers where:

  • Each element is greater than the previous one
  • When you perform bitwise AND on all three elements, the result is 4
  • The last element is as small as possible

The key insight is that for the bitwise AND of all elements to equal x, every bit that is set to 1 in x must be 1 in all array elements. This means x itself must be the first (smallest) element of the array, since any smaller value would lose some of the required 1 bits.

The challenge is then to find the next n-1 values that are greater than x, maintain all the 1 bits from x, and keep the last value as small as possible. This is achieved by strategically setting the 0 bits in x to create the increasing sequence.

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

Intuition

Let's think about the constraints. For the bitwise AND of all elements to equal x, every bit position that has a 1 in x must have a 1 in all array elements. This immediately tells us that the smallest possible first element is x itself - we can't go smaller without losing some required 1 bits.

Now, starting from x, we need to create an increasing sequence of n numbers. The key insight is that we can only modify the bit positions that are 0 in x - these are our "free" bits that we can toggle without affecting the AND result.

Think of it this way: if x in binary is 1001 (with 1s at positions 3 and 0), then every number in our array must have the pattern 1__1, where the underscores can be either 0 or 1. The sequence would look like:

  • 1001 (base value x)
  • 1011 (set one of the free bits)
  • 1101 (set another free bit)
  • 1111 (set both free bits)

This is essentially counting in binary, but only using the positions where x has 0s! If we have k zero bits in x, we can generate up to 2^k different numbers this way.

To minimize the last element, we want to generate exactly n numbers in the most compact way possible. Since the first number is x itself, we need to generate n-1 more numbers. This is equivalent to representing the number n-1 in binary, but spreading its bits across only the positions where x has 0s.

The algorithm becomes: take the binary representation of n-1 and map each of its bits to the available 0 positions in x, filling from the least significant positions first to keep the result as small as possible.

Solution Approach

Based on our intuition, we need to map the bits of n-1 into the zero-bit positions of x. Here's how the implementation works:

First, we decrement n by 1 since we're working with n-1 additional numbers after x:

n -= 1
ans = x

The main algorithm iterates through the first 31 bits (since we're dealing with standard 32-bit integers):

for i in range(31):
    if x >> i & 1 ^ 1:  # Check if bit i of x is 0
        ans |= (n & 1) << i  # Place the LSB of n at position i
        n >>= 1  # Remove the LSB from n

Let's break down this loop:

  1. x >> i & 1 ^ 1 checks if the i-th bit of x is 0 (the XOR with 1 flips the bit, so this is true when the bit is 0)
  2. When we find a 0 bit in x, we:
    • Take the least significant bit of n with (n & 1)
    • Place it at position i in our answer with << i
    • OR it with ans to set that bit
    • Right shift n by 1 to move to the next bit

After processing the first 31 bits, if there are still remaining bits in n (for very large values), we place them starting from position 31:

ans |= n << 31

Example walkthrough:

  • If x = 5 (binary: 101) and n = 3
  • We need to find the 3rd element (index 2) of the array
  • n-1 = 2 (binary: 10)
  • Zero bits in x are at positions 1 and 2
  • We map bit 0 of 2 (which is 0) to position 1 → no change
  • We map bit 1 of 2 (which is 1) to position 2 → set bit 2
  • Result: 101 becomes 1101 (decimal: 13)

This approach ensures we generate the minimum possible value by filling the available zero-bit positions from least significant to most significant, effectively creating the smallest number that maintains all the required 1 bits from x while being the n-th element in our sequence.

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 a concrete example with n = 4 and x = 6.

Step 1: Understanding the constraints

  • We need an array of 4 positive integers in strictly increasing order
  • The bitwise AND of all elements must equal 6 (binary: 110)
  • We want to minimize the last element

Step 2: Identify the bit pattern

  • x = 6 in binary is 110
  • This means bits at positions 2 and 1 must be 1 in ALL array elements
  • Bits at positions 0, 3, 4, ... are "free" - we can set them to create different numbers

Step 3: Build the array

  • First element must be x = 6 itself (binary: 110)
  • We need 3 more numbers (n-1 = 3)

Step 4: Apply the algorithm

  • Start with ans = x = 6 (binary: 110)
  • n-1 = 3 (binary: 11)
  • Find zero-bit positions in x = 6:
    • Position 0: bit is 0
    • Position 1: bit is 1
    • Position 2: bit is 1
    • Position 3: bit is 0
    • Position 4: bit is 0

Step 5: Map bits of n-1 to zero positions

  • Take LSB of 3 (which is 1), place at position 0: 110111
  • Right shift 3 to get 1 (binary: 1)
  • Take LSB of 1 (which is 1), place at position 3: 1111111
  • Right shift 1 to get 0, done

Step 6: Verify the result

  • Final answer: 1111 (binary) = 15 (decimal)
  • The array would be: [6, 7, 14, 15]
    • 6 (binary: 0110)
    • 7 (binary: 0111)
    • 14 (binary: 1110)
    • 15 (binary: 1111)
  • AND of all elements: 0110 & 0111 & 1110 & 1111 = 0110 = 6 ✓

The minimum possible value for the 4th element is 15.

Solution Implementation

1class Solution:
2    def minEnd(self, n: int, x: int) -> int:
3        # Decrement n since we're working with 0-indexed logic
4        n -= 1
5      
6        # Initialize result with the base value x
7        result = x
8      
9        # Process the first 31 bits
10        for bit_position in range(31):
11            # Check if the current bit position in x is 0
12            if (x >> bit_position) & 1 ^ 1:
13                # If x has a 0 at this position, fill it with the next bit from n
14                result |= (n & 1) << bit_position
15                # Move to the next bit of n
16                n >>= 1
17      
18        # Place any remaining bits of n at position 31 and above
19        result |= n << 31
20      
21        return result
22
1class Solution {
2    public long minEnd(int n, int x) {
3        // Decrement n since we're finding the (n-1)th value after x
4        // (x is the 0th value in 0-indexed terms)
5        n--;
6      
7        // Initialize result with x as the base value
8        // This ensures all set bits in x remain set
9        long result = x;
10      
11        // Process the first 31 bits to fill in the gaps (unset bits in x)
12        for (int bitPosition = 0; bitPosition < 31; bitPosition++) {
13            // Check if the current bit position in x is unset (0)
14            if ((x >> bitPosition & 1) == 0) {
15                // If unset, we can use this position to encode bits from n
16                // Take the least significant bit of n and place it at this position
17                result |= (n & 1) << bitPosition;
18                // Remove the least significant bit from n that we just used
19                n >>= 1;
20            }
21        }
22      
23        // Handle remaining bits of n (if any) by placing them starting from bit 31
24        // This handles cases where n has more bits than available gaps in first 31 bits
25        result |= (long) n << 31;
26      
27        return result;
28    }
29}
30
1class Solution {
2public:
3    long long minEnd(int n, int x) {
4        // Decrement n to work with 0-based indexing
5        --n;
6      
7        // Initialize result with the base value x
8        long long ans = x;
9      
10        // Process the first 31 bits
11        for (int i = 0; i < 31; ++i) {
12            // Check if the i-th bit of x is 0
13            if ((x >> i & 1) ^ 1) {
14                // If bit is 0, we can use this position to encode bits from n
15                // Set the i-th bit of ans based on the least significant bit of n
16                ans |= (n & 1) << i;
17                // Remove the processed bit from n
18                n >>= 1;
19            }
20        }
21      
22        // Handle remaining bits of n beyond the 31st position
23        // Cast n to long long to avoid overflow and shift it to position 31
24        ans |= (static_cast<long long>(n)) << 31;
25      
26        return ans;
27    }
28};
29
1/**
2 * Finds the minimum possible value of the last element in an array
3 * where all elements have bitwise AND equal to x
4 * @param n - The number of elements in the array
5 * @param x - The required bitwise AND result of all elements
6 * @returns The minimum possible value of the last element
7 */
8function minEnd(n: number, x: number): number {
9    // Decrement n to work with 0-based indexing
10    --n;
11  
12    // Initialize answer with x as BigInt to handle large numbers
13    let answer: bigint = BigInt(x);
14  
15    // Process the first 31 bits
16    for (let bitPosition = 0; bitPosition < 31; ++bitPosition) {
17        // Check if the current bit position in x is 0
18        if (((x >> bitPosition) & 1) ^ 1) {
19            // If bit is 0 in x, we can set it based on the corresponding bit in n
20            // Set the bit at bitPosition to the least significant bit of n
21            answer |= BigInt(n & 1) << BigInt(bitPosition);
22            // Right shift n to process the next bit
23            n >>= 1;
24        }
25    }
26  
27    // Handle any remaining bits of n beyond the 31st position
28    // Place remaining bits starting from position 31
29    answer |= BigInt(n) << BigInt(31);
30  
31    // Convert the result back to a regular number
32    return Number(answer);
33}
34

Time and Space Complexity

The time complexity is O(log x). The algorithm iterates through at most 31 bits (since we're checking bits from position 0 to 30 in the for loop), which corresponds to the number of bits needed to represent x. Since a 32-bit integer has at most 32 bits, and the number of bits is proportional to log x, the time complexity is O(log x).

The space complexity is O(1). The algorithm only uses a constant amount of extra space for variables ans and the modified n, regardless of the input size. No additional data structures that grow with input size are created.

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

Common Pitfalls

1. Incorrect Bit Checking Logic

A common mistake is incorrectly checking whether a bit in x is 0. The expression x >> i & 1 ^ 1 uses operator precedence that might be confusing.

Pitfall Code:

# Wrong: Misunderstanding operator precedence
if x >> i & 1 ^ 1:  # This actually works but is unclear
  
# Wrong: Incorrect logic
if x >> i & 1:  # This checks if bit is 1, not 0!
  
# Wrong: Missing parentheses leading to confusion
if x >> (i & 1) ^ 1:  # Completely wrong due to precedence

Solution:

# Clear and correct approaches:
if ((x >> i) & 1) == 0:  # Most readable
  
# Or using XOR explicitly with parentheses:
if ((x >> i) & 1) ^ 1:  # Clear precedence
  
# Or using bitwise NOT (be careful with sign extension):
if not ((x >> i) & 1):  # Also clear

2. Forgetting to Decrement n

The problem asks for the n-th element (1-indexed), but the algorithm maps n-1 values into zero bits. Forgetting to decrement n leads to generating the wrong element.

Pitfall Code:

def minEnd(self, n: int, x: int) -> int:
    # Forgot to decrement n!
    result = x
    for i in range(31):
        if ((x >> i) & 1) == 0:
            result |= (n & 1) << i  # Using n directly
            n >>= 1
    return result

Solution:

def minEnd(self, n: int, x: int) -> int:
    n -= 1  # Critical: Convert to 0-indexed
    result = x
    # ... rest of the code

3. Integer Overflow with Large n Values

When n is very large, the remaining bits after processing 31 positions might overflow if not handled properly.

Pitfall Code:

# Might cause issues in languages with fixed integer sizes
result |= n << 31  # If n is large, this could overflow in some contexts

Solution: For Python this isn't an issue due to arbitrary precision integers, but be aware that:

  • In Python, this works correctly
  • In languages like C++ or Java, you'd need to use appropriate data types (long long, BigInteger)
  • Consider adding bounds checking if porting to other languages

4. Off-by-One Error in Bit Position Range

Using the wrong range for bit positions can miss important bits or process unnecessary ones.

Pitfall Code:

# Wrong: Processing too few bits
for i in range(30):  # Missing bit 30
  
# Wrong: Processing too many bits unnecessarily
for i in range(32):  # Bit 31 should be handled separately

Solution:

# Correct: Process bits 0-30 in the loop
for i in range(31):
    # ... bit manipulation
  
# Handle remaining bits separately
result |= n << 31

5. Mutating n Without Preserving Original Value

The algorithm modifies n during execution. If you need the original value later, this becomes a problem.

Pitfall Code:

def minEnd(self, n: int, x: int) -> int:
    n -= 1
    result = x
    for i in range(31):
        if ((x >> i) & 1) == 0:
            result |= (n & 1) << i
            n >>= 1  # Original n value is lost
    # Can't use original n here if needed

Solution:

def minEnd(self, n: int, x: int) -> int:
    idx = n - 1  # Use a different variable
    result = x
    for i in range(31):
        if ((x >> i) & 1) == 0:
            result |= (idx & 1) << i
            idx >>= 1
    result |= idx << 31
    return result
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Depth first search is equivalent to which of the tree traversal order?


Recommended Readings

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

Load More