Problem of The Day: Maximum Swap
Problem Statement
Intuition
The problem asks for the largest possible number that can be obtained by swapping two digits of a given integer. To solve this, the first thought is to find the best two digits to swap, such that the resulting number is maximized. Ideally, we should try to bring the largest possible digit to the most significant position.
Approach
- Decomposition into digits: First, we decompose the number into its digits and store them in a deque. This helps in accessing the digits easily from both ends.
-
Tracking the largest digits: A max-heap is used to store the digits along with their indices. Since we are aiming to maximize the number, the max-heap allows us to always get the largest digit that hasn’t been swapped yet.
-
Swapping for maximization: For each digit (starting from the most significant position), we check if there is a larger digit available at a later position. If such a digit is found, we perform the swap and immediately compute the new number formed by the updated digits.
-
Heap management: The heap helps efficiently track the largest digit, and we use the digit’s index to compare with the current digits.
- Return the largest number: If no beneficial swap can be made, we return the original number.
Complexity
- Time complexity:
The time complexity is dominated by the need to traverse the digits and manage the max-heap. Building the max-heap takes \(O(n \log n)\), where \(n\) is the number of digits in the number. Thus, the overall time complexity is \(O(n \log n)\). - Space complexity:
The space complexity is \(O(n)\), where \(n\) is the number of digits, because of the storage used for the deque and the max-heap.
Code
class Solution:
def maximumSwap(self, num: int) -> int:
temp = num
max_heap = []
queue = deque()
i = -1
while num > 0:
digit = num % 10
heapq.heappush(max_heap, (-digit, -i, i))
num = num // 10
i -= 1
queue.appendleft(digit)
N = len(queue)
while max_heap:
digit, _, i = heapq.heappop(max_heap)
digit *= -1
for j in range(0, N + i + 1):
if queue[j] < digit:
queue[i], queue[j] = queue[j], queue[i]
ret = 0
for x in queue:
ret = ret * 10 + x
return ret
return temp
Editorial
Approach 2: Greedy Two-Pass
class Solution:
def maximumSwap(self, num: int) -> int:
num_str = list(str(num))
n = len(num_str)
max_right_index = [0] * n
max_right_index[n - 1] = n - 1
for i in range(n - 2, -1, -1):
max_right_index[i] = (
i
if num_str[i] > num_str[max_right_index[i + 1]]
else max_right_index[i + 1]
)
for i in range(n):
if num_str[i] < num_str[max_right_index[i]]:
num_str[i], num_str[max_right_index[i]] = (
num_str[max_right_index[i]],
num_str[i],
)
return int("".join(num_str))
return num
- time: O(n)
- space: O(n)
Approach 3: Suboptimal Greedy
class Solution:
def maximumSwap(self, num: int) -> int:
num_str = str(num)
n = len(num_str)
last_seen = [-1] * 10 # Store the last occurrence of each digit
# Record the last occurrence of each digit
for i in range(n):
last_seen[int(num_str[i])] = i
# Traverse the string to find the first digit that can be swapped with a larger one
for i in range(n):
for d in range(9, int(num_str[i]), -1):
if last_seen[d] > i:
# Perform the swap
num_str = list(num_str)
num_str[i], num_str[last_seen[d]] = (
num_str[last_seen[d]],
num_str[i],
)
num_str = "".join(num_str)
return int(
num_str
) # Return the new number immediately after the swap
return num # Return the original number if no swap can maximize it
Approach 4: Space Optimized Greedy
class Solution:
def maximumSwap(self, num: int) -> int:
num_str = list(str(num))
n = len(num_str)
max_digit_index = -1
swap_idx_1 = -1
swap_idx_2 = -1
# Traverse the string from right to left, tracking the max digit and
# potential swap
for i in range(n - 1, -1, -1):
if max_digit_index == -1 or num_str[i] > num_str[max_digit_index]:
max_digit_index = i # Update the index of the max digit
elif num_str[i] < num_str[max_digit_index]:
swap_idx_1 = i # Mark the smaller digit for swapping
swap_idx_2 = (
max_digit_index # Mark the larger digit for swapping
)
# Perform the swap if a valid swap is found
if swap_idx_1 != -1 and swap_idx_2 != -1:
num_str[swap_idx_1], num_str[swap_idx_2] = (
num_str[swap_idx_2],
num_str[swap_idx_1],
)
return int(
"".join(num_str)
) # Return the new number or the original if no
# swap occurred