Problem of The Day: Minimize XOR
Problem Statement
Intuition
The problem is to construct a number that has the same number of set bits (1s) as num2
, while minimizing the XOR result with num1
.
The key observation is that XOR minimizes when the constructed number (res
) has its bits as close as possible to num1
. To achieve this, prioritize setting the most significant bits of num1
in res
while respecting the number of set bits required.
Approach
-
Count the set bits in
num2
:- Use bitwise operations to calculate the number of 1s in
num2
(set_bit
).
- Use bitwise operations to calculate the number of 1s in
-
Identify the leftmost bit position in
num1
:- Iterate through the bits of
num1
to find its most significant bit.
- Iterate through the bits of
-
Construct the result:
- Start with
res = 0
and try to set bits inres
from most significant to least significant:- Use the bits of
num1
when they are set. - Stop setting bits from
num1
when the required number of set bits (set_bit
) is satisfied.
- Use the bits of
- If additional bits need to be set after using all possible bits from
num1
, set the least significant bits inres
.
- Start with
-
Return the result:
- The constructed number (
res
) minimizes the XOR value withnum1
and satisfies the set bit condition.
- The constructed number (
Complexity
-
Time Complexity:
\(O(\log(\text{max}(num1, num2)))\)- Counting set bits and iterating through bits of
num1
are logarithmic in the value of the number.
- Counting set bits and iterating through bits of
-
Space Complexity:
\(O(1)\)- No additional data structures are used; the space usage is constant.
Code
class Solution:
def minimizeXor(self, num1: int, num2: int) -> int:
res = 0
set_bit = 0
x, y = num1, num2
# Count the number of set bits in num2
while y > 0:
set_bit += (y & 1)
y = y >> 1
# Find the leftmost bit of num1
left_most_bit_num1 = 1
while x > 1:
x = x >> 1
left_most_bit_num1 = left_most_bit_num1 << 1
# Set bits in res using num1's bits
res = 0
while set_bit > 0 and left_most_bit_num1 > 0:
if left_most_bit_num1 & num1 == left_most_bit_num1:
res = res | left_most_bit_num1
set_bit -= 1
left_most_bit_num1 = left_most_bit_num1 >> 1
# Set remaining bits from the least significant position
curr = 1
while set_bit > 0:
if res & curr != curr:
res = res | curr
set_bit -= 1
curr = curr << 1
return res
Editorial
Trick
To check if the i-th bit of num is set:
Shift 1 left by i (1 << i) positions to isolate the i-th bit.
Perform a bitwise AND: num & (1 << i).
If the result is not 0, the i-th bit of num is set.
----------------------------------------------------------
To set the i-th bit of num:
Shift 1 left by i (1 << i) positions to isolate the i-th bit.
Perfom a bitwise OR: num | (1 << i).
----------------------------------------------------------
To unset the i-th bit of num:
Shift 1 left by i (1 << i) positions to create a mask.
Invert the mask using ~(1 << i) to make the i-th bit 1 and all the other bits 0.
Perform a bitwise AND: num & ~(1 << i).
Approach 1: From Optimal to Valid
class Solution:
def minimizeXor(self, num1: int, num2: int) -> int:
# Initialize result to num1. We will modify result.
result = num1
target_set_bits_count = bin(num2).count("1")
set_bits_count = bin(result).count("1")
# Start with the least significant bit (bit 0).
current_bit = 0
# Add bits to result if it has fewer set bits than the target.
while set_bits_count < target_set_bits_count:
# If the current bit in result is not set (0), set it to 1.
if not self._is_set(result, current_bit):
result = self._set_bit(result, current_bit)
set_bits_count += 1
# Move to the next bit.
current_bit += 1
# Remove bits from result if it has more set bits than the target.
while set_bits_count > target_set_bits_count:
# If the current bit in result is set (1), unset it (make it 0).
if self._is_set(result, current_bit):
result = self._unset_bit(result, current_bit)
set_bits_count -= 1
# Move to the next bit.
current_bit += 1
return result
# Helper function to check if the given bit position in result is set (1).
def _is_set(self, x: int, bit: int) -> bool:
return (x & (1 << bit)) != 0
# Helper function to set the given bit position in result to 1.
def _set_bit(self, x: int, bit: int):
return x | (1 << bit)
# Helper function to unset the given bit position in x (set it to 0).
def _unset_bit(self, x: int, bit: int):
return x & ~(1 << bit)