2429. Minimize XOR
Problem Description
You are given two positive integers num1
and num2
. Your task is to find a positive integer x
that satisfies two conditions:
- The integer
x
must have the exact same number of set bits (1s in binary representation) asnum2
- The XOR operation between
x
andnum1
should produce the smallest possible result
The XOR operation compares bits at each position - it returns 1 when bits are different and 0 when they are the same.
For example, if num1 = 3
(binary: 11) and num2 = 5
(binary: 101), then num2
has 2 set bits. We need to find an integer x
with exactly 2 set bits such that x XOR num1
is minimized.
To minimize x XOR num1
, we want x
to be as similar to num1
as possible. The strategy is:
- First, try to match the 1-bits in
num1
by setting those same positions to 1 inx
(this makes those positions become 0 after XOR) - If we still need more set bits to match the count from
num2
, add 1s in positions wherenum1
has 0s, starting from the least significant positions
The problem guarantees that there is a unique solution for each test case.
Intuition
To minimize x XOR num1
, we need to understand what XOR does - it produces 0 when bits match and 1 when they differ. So to get the smallest XOR result, we want as many 0s as possible in the result, especially in the higher bit positions (since they contribute more to the final value).
The key insight is that we get a 0 in the XOR result when x
and num1
have the same bit value at that position. Since we want to minimize the XOR result, we should prioritize matching the higher-order bits of num1
first.
Here's the greedy strategy:
-
Match the 1-bits from high to low: Start from the most significant bits of
num1
. Wherevernum1
has a 1, we should also put a 1 inx
at that position (if we still have set bits to use). This ensures these positions become 0 after XOR, contributing nothing to the result. -
Handle remaining set bits: If we've matched all the 1s in
num1
but still need more set bits to reach our target count (fromnum2
), we must add 1s wherenum1
has 0s. To minimize impact, we add these from the least significant positions upward, since lower bits contribute less to the final value.
For example, if num1 = 1100
(binary) and we need 3 set bits total:
- First, match the two 1s in
num1
:x
becomes1100
(used 2 set bits) - Still need 1 more set bit, so add it at the lowest available 0 position:
x
becomes1101
1100 XOR 1101 = 0001
, which is minimal
This greedy approach works because placing 1s where num1
already has 1s "cancels them out" in XOR, while any additional 1s we're forced to add should be in the least significant positions to minimize their contribution.
Learn more about Greedy patterns.
Solution Approach
The implementation follows the greedy strategy we identified, using bit manipulation to efficiently construct the result:
Step 1: Count the required set bits
cnt = num2.bit_count()
We first determine how many 1-bits we need in our answer x
by counting the set bits in num2
.
Step 2: Match 1-bits from high to low
x = 0
for i in range(30, -1, -1):
if num1 >> i & 1 and cnt:
x |= 1 << i
cnt -= 1
We iterate through bit positions from 30 down to 0 (covering all bits in a 32-bit integer). For each position i
:
num1 >> i & 1
checks if biti
ofnum1
is 1- If it is 1 and we still have set bits to place (
cnt > 0
), we set biti
inx
usingx |= 1 << i
- We decrement
cnt
after placing each bit
This ensures we first match all the 1-bits in num1
from most significant to least significant, minimizing the higher-order bits in the XOR result.
Step 3: Place remaining bits in 0 positions
for i in range(30):
if num1 >> i & 1 ^ 1 and cnt:
x |= 1 << i
cnt -= 1
If we still need more set bits after matching all 1s in num1
, we iterate from bit 0 upward:
num1 >> i & 1 ^ 1
checks if biti
ofnum1
is 0 (using XOR with 1 to flip the bit check)- If it's 0 and we still need set bits (
cnt > 0
), we set biti
inx
- We decrement
cnt
for each bit placed
This places any remaining required 1-bits in the lowest available positions where num1
has 0s, minimizing their contribution to the final XOR value.
The algorithm runs in O(1) time since we iterate through at most 31 bits twice, and uses O(1) space to store the result.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through an example with num1 = 11
(binary: 1011) and num2 = 13
(binary: 1101).
Step 1: Count required set bits
num2 = 13
has binary representation1101
- Count of set bits = 3
- So we need to construct
x
with exactly 3 set bits
Step 2: Match 1-bits from high to low in num1
num1 = 11
has binary representation1011
- Scanning from high to low (bit positions 30 down to 0):
- Bit 3:
num1
has 1 β set bit 3 inx
, nowx = 1000
, cnt = 2 - Bit 2:
num1
has 0 β skip - Bit 1:
num1
has 1 β set bit 1 inx
, nowx = 1010
, cnt = 1 - Bit 0:
num1
has 1 β set bit 0 inx
, nowx = 1011
, cnt = 0
- Bit 3:
Step 3: Place remaining bits (if any)
- We've used all 3 required bits, so cnt = 0
- No additional bits needed
Result:
x = 1011
(decimal: 11)x XOR num1 = 1011 XOR 1011 = 0000 = 0
The XOR result is 0, which is the minimum possible value. This makes sense because we had enough set bits in num1
(3 bits) to match our requirement exactly.
Another example: num1 = 3
(binary: 11) and num2 = 5
(binary: 101)
Step 1: num2
has 2 set bits, so we need exactly 2 set bits in x
Step 2: Match 1-bits in num1
from high to low:
- Bit 1:
num1
has 1 β set bit 1 inx
, nowx = 10
, cnt = 1 - Bit 0:
num1
has 1 β set bit 0 inx
, nowx = 11
, cnt = 0
Step 3: cnt = 0, no additional bits needed
Result: x = 11
(decimal: 3), and 3 XOR 3 = 0
Example with remaining bits: num1 = 4
(binary: 100) and num2 = 7
(binary: 111)
Step 1: num2
has 3 set bits
Step 2: Match 1-bits in num1
:
- Bit 2:
num1
has 1 β set bit 2 inx
, nowx = 100
, cnt = 2
Step 3: Still need 2 more bits, add from lowest 0-positions:
- Bit 0:
num1
has 0 β set bit 0 inx
, nowx = 101
, cnt = 1 - Bit 1:
num1
has 0 β set bit 1 inx
, nowx = 111
, cnt = 0
Result: x = 111
(decimal: 7), and 100 XOR 111 = 011 = 3
Solution Implementation
1class Solution:
2 def minimizeXor(self, num1: int, num2: int) -> int:
3 # Count the number of set bits (1s) in num2
4 # This is the number of set bits that x must have
5 remaining_bits = num2.bit_count()
6
7 # Initialize result x to 0
8 x = 0
9
10 # First pass: Set bits in x where num1 has 1s (from MSB to LSB)
11 # This minimizes XOR by matching 1s in num1
12 for bit_position in range(30, -1, -1):
13 # Check if current bit in num1 is set and we still need to set bits
14 if (num1 >> bit_position) & 1 and remaining_bits > 0:
15 # Set the bit at current position in x
16 x |= 1 << bit_position
17 remaining_bits -= 1
18
19 # Second pass: If we still need more set bits, set them where num1 has 0s (from LSB to MSB)
20 # Setting bits where num1 has 0s increases XOR, so we do this from LSB for minimal increase
21 for bit_position in range(30):
22 # Check if current bit in num1 is 0 and we still need to set bits
23 if ((num1 >> bit_position) & 1) ^ 1 and remaining_bits > 0:
24 # Set the bit at current position in x
25 x |= 1 << bit_position
26 remaining_bits -= 1
27
28 return x
29
1class Solution {
2 public int minimizeXor(int num1, int num2) {
3 // Count the number of set bits (1s) in num2
4 // The result must have the same number of set bits
5 int setBitsCount = Integer.bitCount(num2);
6
7 // Initialize the result value
8 int result = 0;
9
10 // Phase 1: Set bits from high to low where num1 has 1s
11 // This minimizes XOR by matching the most significant bits first
12 for (int bitPosition = 30; bitPosition >= 0 && setBitsCount > 0; bitPosition--) {
13 // Check if num1 has a 1 at the current bit position
14 if ((num1 >> bitPosition & 1) == 1) {
15 // Set this bit in the result to minimize XOR difference
16 result |= 1 << bitPosition;
17 setBitsCount--;
18 }
19 }
20
21 // Phase 2: If we still need more set bits, add them from low to high
22 // Choose positions where num1 has 0s to minimize the XOR value
23 for (int bitPosition = 0; setBitsCount > 0; bitPosition++) {
24 // Check if num1 has a 0 at the current bit position
25 if ((num1 >> bitPosition & 1) == 0) {
26 // Set this bit in the result
27 result |= 1 << bitPosition;
28 setBitsCount--;
29 }
30 }
31
32 return result;
33 }
34}
35
1class Solution {
2public:
3 int minimizeXor(int num1, int num2) {
4 // Count the number of set bits in num2
5 int bitsToSet = __builtin_popcount(num2);
6 int result = 0;
7
8 // Phase 1: Set bits in result where num1 has set bits (from MSB to LSB)
9 // This minimizes XOR by matching as many significant bits as possible
10 for (int bitPos = 30; bitPos >= 0 && bitsToSet > 0; --bitPos) {
11 // Check if the bit at position bitPos is set in num1
12 if ((num1 >> bitPos) & 1) {
13 // Set the corresponding bit in result
14 result |= (1 << bitPos);
15 --bitsToSet;
16 }
17 }
18
19 // Phase 2: If we still need to set more bits, set them where num1 has 0s
20 // Start from LSB to minimize the value increase
21 for (int bitPos = 0; bitsToSet > 0; ++bitPos) {
22 // Check if the bit at position bitPos is NOT set in num1
23 if (((num1 >> bitPos) & 1) == 0) {
24 // Set the corresponding bit in result
25 result |= (1 << bitPos);
26 --bitsToSet;
27 }
28 }
29
30 return result;
31 }
32};
33
1/**
2 * Minimizes XOR between a number and num1 while maintaining the same number of set bits as num2
3 * @param num1 - The target number to minimize XOR with
4 * @param num2 - The number whose set bit count to match
5 * @returns The number x that minimizes XOR with num1 and has same number of set bits as num2
6 */
7function minimizeXor(num1: number, num2: number): number {
8 // Count the number of set bits (1s) in num2
9 let setBitCount: number = 0;
10 while (num2 > 0) {
11 // Clear the rightmost set bit using Brian Kernighan's algorithm
12 num2 &= num2 - 1;
13 setBitCount++;
14 }
15
16 // Initialize the result number
17 let result: number = 0;
18
19 // First pass: Set bits in result where num1 has set bits (from MSB to LSB)
20 // This minimizes XOR by matching as many 1s as possible
21 for (let bitPosition: number = 30; bitPosition >= 0 && setBitCount > 0; bitPosition--) {
22 // Check if the current bit position in num1 is set
23 if ((num1 >> bitPosition) & 1) {
24 // Set the same bit position in result
25 result |= 1 << bitPosition;
26 setBitCount--;
27 }
28 }
29
30 // Second pass: If we still need more set bits, set them in positions where num1 has 0s
31 // Start from LSB to minimize the value while maintaining the required bit count
32 for (let bitPosition: number = 0; setBitCount > 0; bitPosition++) {
33 // Check if the current bit position in num1 is not set
34 if (!((num1 >> bitPosition) & 1)) {
35 // Set this bit position in result
36 result |= 1 << bitPosition;
37 setBitCount--;
38 }
39 }
40
41 return result;
42}
43
Time and Space Complexity
The time complexity is O(log n)
, where n
is the maximum value of num1
and num2
. This is because:
- The first loop iterates from bit position 30 down to 0, which is
O(31) = O(1)
iterations in terms of constant operations, but representsO(log n)
in terms of the number of bits needed to represent the maximum valuen
- The second loop iterates from bit position 0 to 29, which similarly is
O(30) = O(1)
constant iterations, but representsO(log n)
bit positions - The
bit_count()
operation onnum2
also takesO(log n)
time as it needs to count set bits in the binary representation - Each iteration performs constant time operations: bit shifting (
>>
), bitwise AND (&
), bitwise OR (|
), and bitwise XOR (^
)
The space complexity is O(1)
because:
- Only a fixed number of integer variables are used:
cnt
andx
- The loop variable
i
also uses constant space - No additional data structures that grow with input size are created
- All operations are performed in-place using bitwise operations
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrect Bit Range or Off-by-One Errors
A common mistake is using an incorrect range for bit positions. Since the problem states positive integers and typical constraints go up to 10^9 (which requires 30 bits), using range(31) or range(32) might seem correct, but could lead to issues:
Pitfall Example:
# Incorrect: might check unnecessary bits
for i in range(31, -1, -1): # Checking bit 31 when not needed
if num1 >> i & 1 and cnt:
x |= 1 << i
cnt -= 1
Solution: Determine the actual bit range needed. For most problems with constraint up to 10^9, 30 bits suffice:
# Correct: Check only necessary bits (0-29 for numbers up to 10^9)
for i in range(30, -1, -1):
if num1 >> i & 1 and cnt:
x |= 1 << i
cnt -= 1
2. Operator Precedence Confusion
Bitwise operators have lower precedence than comparison operators, which can cause unexpected behavior:
Pitfall Example:
# Incorrect: This evaluates as (num1 >> i) & (1 and cnt) if num1 >> i & 1 and cnt: # Works but unclear x |= 1 << i # Even worse: forgetting parentheses in XOR check if num1 >> i & 1 ^ 1 and cnt: # XOR has lower precedence than &
Solution: Use parentheses for clarity and correctness:
# Clear and correct if ((num1 >> i) & 1) and cnt > 0: x |= 1 << i cnt -= 1 # For checking if bit is 0 if ((num1 >> i) & 1) == 0 and cnt > 0: x |= 1 << i cnt -= 1
3. Not Handling Edge Cases
Failing to consider when num2
has more or fewer set bits than num1
:
Pitfall Example:
# Might not work if num2.bit_count() > num1.bit_count()
# Only tries to match existing 1s in num1
for i in range(30, -1, -1):
if num1 >> i & 1:
x |= 1 << i
# Forgets to add remaining bits in 0 positions
Solution: Always implement both phases - matching 1s and adding to 0s:
cnt = num2.bit_count()
x = 0
# Phase 1: Match 1s in num1
for i in range(30, -1, -1):
if (num1 >> i) & 1 and cnt > 0:
x |= 1 << i
cnt -= 1
# Phase 2: Add remaining bits to 0 positions
for i in range(30):
if ((num1 >> i) & 1) == 0 and cnt > 0:
x |= 1 << i
cnt -= 1
4. Using Wrong Direction in Second Pass
Setting bits in the wrong order when adding to 0 positions:
Pitfall Example:
# Incorrect: Adding from MSB to LSB in second pass
for i in range(30, -1, -1): # Wrong direction!
if ((num1 >> i) & 1) == 0 and cnt > 0:
x |= 1 << i
cnt -= 1
Solution: Add remaining bits from LSB to MSB to minimize the XOR value:
# Correct: Add from LSB (0) to MSB (29)
for i in range(30):
if ((num1 >> i) & 1) == 0 and cnt > 0:
x |= 1 << i
cnt -= 1
This ensures that any additional 1s contribute the smallest possible values to the XOR result.
Given a sorted array of integers and an integer called target, find the element that
equals to the target and return its index. Select the correct code that fills the
___
in the given code snippet.
1def binary_search(arr, target):
2 left, right = 0, len(arr) - 1
3 while left ___ right:
4 mid = (left + right) // 2
5 if arr[mid] == target:
6 return mid
7 if arr[mid] < target:
8 ___ = mid + 1
9 else:
10 ___ = mid - 1
11 return -1
12
1public static int binarySearch(int[] arr, int target) {
2 int left = 0;
3 int right = arr.length - 1;
4
5 while (left ___ right) {
6 int mid = left + (right - left) / 2;
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
1function binarySearch(arr, target) {
2 let left = 0;
3 let right = arr.length - 1;
4
5 while (left ___ right) {
6 let mid = left + Math.trunc((right - left) / 2);
7 if (arr[mid] == target) return mid;
8 if (arr[mid] < target) {
9 ___ = mid + 1;
10 } else {
11 ___ = mid - 1;
12 }
13 }
14 return -1;
15}
16
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
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
Recursion Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask
Want a Structured Path to Master System Design Too? Donβt Miss This!