Facebook Pixel

3226. Number of Bit Changes to Make Two Integers Equal

EasyBit Manipulation
Leetcode Link

Problem Description

You are given two positive integers n and k.

The problem allows you to perform a specific operation: you can select any bit in the binary representation of n that is currently 1 and change it to 0. You can perform this operation multiple times.

Your goal is to determine the minimum number of such changes needed to transform n into k. If it's impossible to achieve this transformation, you should return -1.

For example, if n = 13 (binary: 1101) and k = 4 (binary: 0100), you could change the bits at positions 0, 2, and 3 from 1 to 0, resulting in 0100 which equals k. This would require 3 changes.

The key constraint is that you can only change 1 bits to 0 bits - you cannot change 0 bits to 1 bits. This means if k has a 1 bit in any position where n has a 0 bit, the transformation is impossible.

The solution checks if n & k == k to verify that every 1 bit in k has a corresponding 1 bit in n. If this condition is met, the number of changes needed equals the count of 1 bits in n ^ k, which represents the positions where n and k differ.

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

Intuition

To understand this problem, let's think about what operations we can and cannot do. We can only change 1 bits to 0 bits in n, but we cannot change 0 bits to 1 bits.

This immediately gives us a critical insight: if k has a 1 bit at any position where n has a 0 bit, it's impossible to transform n into k. Why? Because we would need to change a 0 to a 1, which is not allowed.

How can we check this condition? By using the bitwise AND operation n & k. If n & k == k, it means that every 1 bit in k has a corresponding 1 bit in n. This is because AND operation only results in 1 when both bits are 1. If this condition fails, we return -1.

Once we know the transformation is possible, we need to count how many bits we need to change. These are the positions where n has a 1 but k has a 0.

The XOR operation n ^ k gives us exactly the positions where n and k differ - it results in 1 wherever the bits are different. Since we already know that all 1 bits in k exist in n, any difference must be a position where n has 1 and k has 0 - precisely the bits we need to change!

Therefore, counting the number of 1 bits in n ^ k gives us the minimum number of changes needed. The .bit_count() method efficiently counts these 1 bits, giving us our answer.

Solution Approach

The solution uses bit manipulation to efficiently solve this problem in a single line of code. Let's break down the implementation:

return -1 if n & k != k else (n ^ k).bit_count()

Step 1: Check if transformation is possible

First, we verify if the transformation from n to k is feasible using n & k != k:

  • The bitwise AND operation n & k preserves only the bits that are 1 in both numbers
  • If n & k == k, it means every 1 bit in k has a corresponding 1 bit in n
  • If n & k != k, there's at least one position where k has 1 but n has 0, making transformation impossible
  • In this case, we return -1

Step 2: Count the required changes

If transformation is possible, we calculate the number of changes using (n ^ k).bit_count():

  • The XOR operation n ^ k produces a number where each bit is 1 if the corresponding bits in n and k are different
  • Since we've already verified that all 1 bits in k exist in n, any difference must be where n has 1 and k has 0
  • These are exactly the bits we need to change from 1 to 0
  • The .bit_count() method counts the number of 1 bits in the XOR result, giving us the minimum number of changes needed

Time Complexity: O(1) - The bitwise operations and bit counting are constant time operations for integers within the typical range.

Space Complexity: O(1) - We only use a constant amount of extra space.

This elegant solution leverages the properties of bitwise operations to solve the problem without explicitly iterating through each bit position.

Ready to land your dream job?

Unlock your dream job with a 3-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with n = 13 and k = 4.

Step 1: Convert to binary representation

  • n = 13 → binary: 1101
  • k = 4 → binary: 0100

Step 2: Check if transformation is possible using n & k == k

  1101 (n = 13)
& 0100 (k = 4)
-------
  0100 (result = 4)

Since n & k = 4 and k = 4, we have n & k == k, so transformation is possible!

This makes sense because the only 1 bit in k (at position 2) has a corresponding 1 bit in n.

Step 3: Count the bits that need to change using (n ^ k).bit_count()

  1101 (n = 13)
^ 0100 (k = 4)
-------
  1001 (result = 9)

The XOR gives us 1001, which has two 1 bits (at positions 0 and 3).

These represent:

  • Position 0: n has 1, k has 0 → need to change
  • Position 3: n has 1, k has 0 → need to change

Counting the 1 bits in 1001: 2 changes needed

Verification: Starting from 1101, if we change bits at positions 0 and 3 to 0:

  • 11011100 (change position 0) → 0100 (change position 3)
  • Final result: 0100 = 4 ✓

The answer is 2.

Solution Implementation

1class Solution:
2    def minChanges(self, n: int, k: int) -> int:
3        # Check if k can be obtained from n by only turning bits from 1 to 0
4        # This is true when all bits that are 1 in k are also 1 in n
5        # In other words, (n & k) should equal k
6        if n & k != k:
7            # If k has any bit set to 1 where n has 0, transformation is impossible
8            return -1
9      
10        # Calculate the XOR of n and k to find differing bits
11        # These are the bits that need to be changed (turned from 1 to 0 in n)
12        differing_bits = n ^ k
13      
14        # Count the number of 1s in the XOR result
15        # This gives us the minimum number of bit changes needed
16        min_changes = differing_bits.bit_count()
17      
18        return min_changes
19
1class Solution {
2    public int minChanges(int n, int k) {
3        // Check if k can be formed from n by only turning off bits
4        // If (n & k) != k, it means k has some bits set to 1 where n has 0
5        // This is impossible since we can only change 1s to 0s, not 0s to 1s
6        if ((n & k) != k) {
7            return -1;
8        }
9      
10        // Calculate the number of bits that need to be changed from 1 to 0
11        // XOR gives us the bits that are different between n and k
12        // Since we already verified k is achievable, these different bits
13        // represent positions where n has 1 and k has 0
14        int differentBits = n ^ k;
15      
16        // Count the number of 1s in the XOR result
17        // This gives us the minimum number of changes needed
18        return Integer.bitCount(differentBits);
19    }
20}
21
1class Solution {
2public:
3    int minChanges(int n, int k) {
4        // Check if k can be formed from n by only turning bits from 1 to 0
5        // This is possible only if all bits that are 1 in k are also 1 in n
6        // (n & k) == k verifies this condition
7        if ((n & k) != k) {
8            return -1;  // Impossible to transform n to k
9        }
10      
11        // Count the number of bits that need to be changed (1 to 0)
12        // XOR gives us the positions where n and k differ
13        // Since we already verified k's 1-bits are subset of n's 1-bits,
14        // the XOR result only contains bits that are 1 in n but 0 in k
15        int bitsToChange = __builtin_popcount(n ^ k);
16      
17        return bitsToChange;
18    }
19};
20
1/**
2 * Calculates the minimum number of bit changes needed to transform n into k
3 * @param n - The source number to transform
4 * @param k - The target number to achieve
5 * @returns The minimum number of bit changes, or -1 if transformation is impossible
6 */
7function minChanges(n: number, k: number): number {
8    // Check if k can be obtained from n by only changing 1s to 0s
9    // If (n & k) !== k, it means k has 1s where n has 0s, which is impossible
10    if ((n & k) !== k) {
11        return -1;
12    }
13  
14    // Count the number of bits that need to be changed (1s in n that are 0s in k)
15    // XOR gives us the differing bits, and we count them
16    return bitCount(n ^ k);
17}
18
19/**
20 * Counts the number of set bits (1s) in a number using Brian Kernighan's algorithm
21 * This is an optimized bit counting algorithm that works in O(log n) time
22 * @param i - The number to count bits in
23 * @returns The count of set bits (1s) in the binary representation
24 */
25function bitCount(i: number): number {
26    // Step 1: Count bits in groups of 2
27    // Subtract the number of 1s in odd positions
28    i = i - ((i >>> 1) & 0x55555555);
29  
30    // Step 2: Count bits in groups of 4
31    // Add pairs of 2-bit counts to get 4-bit counts
32    i = (i & 0x33333333) + ((i >>> 2) & 0x33333333);
33  
34    // Step 3: Count bits in groups of 8
35    // Add pairs of 4-bit counts to get 8-bit counts
36    i = (i + (i >>> 4)) & 0x0f0f0f0f;
37  
38    // Step 4: Count bits in groups of 16
39    // Add pairs of 8-bit counts
40    i = i + (i >>> 8);
41  
42    // Step 5: Count bits in groups of 32
43    // Add pairs of 16-bit counts
44    i = i + (i >>> 16);
45  
46    // Return only the lowest 6 bits (maximum count for 32-bit number is 32, which fits in 6 bits)
47    return i & 0x3f;
48}
49

Time and Space Complexity

The time complexity of this solution is O(log n). This is because:

  • The bitwise AND operation n & k takes O(log n) time as it processes the binary representation of n bit by bit
  • The XOR operation n ^ k also takes O(log n) time for the same reason
  • The bit_count() method counts the number of set bits (1s) in the binary representation, which requires examining each bit position, taking O(log n) time

The space complexity is O(1). The algorithm only uses a constant amount of extra space for:

  • Storing the result of n & k comparison
  • Storing the result of n ^ k
  • The internal counter for bit_count()

All operations are performed in-place without requiring additional space that scales with the input size.

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

Common Pitfalls

1. Misunderstanding the Direction of Bit Changes

A common mistake is thinking you can change bits in both directions (0→1 and 1→0), leading to incorrect validation logic.

Incorrect approach:

def minChanges(self, n: int, k: int) -> int:
    # Wrong: This counts all differences without checking feasibility
    return (n ^ k).bit_count()

Why it fails: This doesn't check if transformation is possible. For example, with n = 4 (binary: 0100) and k = 7 (binary: 0111), this would return 2, but the transformation is actually impossible since we can't change 0s to 1s.

Correct approach:

def minChanges(self, n: int, k: int) -> int:
    # Must verify k's 1-bits are subset of n's 1-bits
    if n & k != k:
        return -1
    return (n ^ k).bit_count()

2. Using the Wrong Validation Condition

Some might check if n >= k thinking larger numbers have "more bits", but this is incorrect for binary operations.

Incorrect approach:

def minChanges(self, n: int, k: int) -> int:
    # Wrong: Numerical comparison doesn't ensure bit compatibility
    if n < k:
        return -1
    return (n ^ k).bit_count()

Why it fails: Consider n = 8 (binary: 1000) and k = 7 (binary: 0111). Here n > k, but transformation is impossible because k has 1s where n has 0s.

Correct validation: Always use n & k == k to ensure every 1-bit in k exists in n.

3. Manual Bit Counting Instead of Built-in Methods

Implementing custom bit counting can introduce bugs and inefficiency.

Error-prone approach:

def minChanges(self, n: int, k: int) -> int:
    if n & k != k:
        return -1
  
    # Manual counting - prone to errors
    xor_result = n ^ k
    count = 0
    while xor_result:
        count += 1
        xor_result &= xor_result - 1  # Clear rightmost bit
    return count

Better approach: Use Python's built-in bit_count() method which is both cleaner and less error-prone:

return (n ^ k).bit_count()

4. Confusing Bitwise AND with OR for Validation

Using OR instead of AND for checking bit compatibility is a subtle but critical error.

Incorrect approach:

def minChanges(self, n: int, k: int) -> int:
    # Wrong: OR doesn't verify subset relationship
    if n | k != n:
        return -1
    return (n ^ k).bit_count()

Why it fails: The condition n | k == n checks if k doesn't add any new 1-bits to n, but this is backwards - it would accept cases where k has 0s that we'd need to turn into 1s, which is impossible.

Remember: Use n & k == k to verify that k's bit pattern is achievable from n by only turning 1s to 0s.

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

Which of the following array represent a max heap?


Recommended Readings

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

Load More