670. Maximum Swap
Problem Description
You are given an integer num
. Your task is to find the maximum possible value you can obtain by swapping at most two digits in the number.
The key constraints are:
- You can swap two digits at most once (or not swap at all if the number is already maximum)
- The swap involves exchanging the positions of exactly two digits
- You want to maximize the resulting number
For example:
- If
num = 2736
, you can swap the digits7
and2
to get7236
, which is the maximum possible value - If
num = 9973
, the number is already at its maximum, so no swap is needed and you return9973
The algorithm works by:
- Converting the number to a string to easily access individual digits
- Creating an array
d
that tracks, for each position, the index of the largest digit to its right (including itself) - Finding the leftmost position where a smaller digit can be swapped with a larger digit from the right
- Performing the swap and converting back to an integer
The greedy approach ensures we get the maximum value by:
- Swapping the leftmost smaller digit with the rightmost occurrence of the largest available digit to its right
- This maximizes the impact on the number's value since digits on the left have higher place values
Intuition
To maximize a number by swapping at most two digits, we need to think about what makes a number larger. The leftmost (most significant) digits have the greatest impact on the number's value. So ideally, we want the largest digits to appear as far left as possible.
The key insight is that we should find the first position from the left where we can place a larger digit. To do this optimally, we need to:
-
Find which larger digit to use: For any position, we want to know what's the largest digit available to its right that we could potentially swap with. But we can't just pick any large digit - we need to track where the largest digits are located.
-
Choose the best swap position: We scan from left to right to find the first position where the current digit is smaller than the maximum digit available to its right. This ensures we're making the most impactful improvement to the number's value.
-
Handle ties carefully: When multiple positions have the same maximum digit value, we prefer the rightmost one. Why? Because if we're swapping with position
i
, we want to move the smallest possible value from the right side to positioni
, minimizing what we lose from that position.
This leads us to the approach of:
- First, preprocessing the string to build an array
d
whered[i]
stores the index of the maximum digit from positioni
to the end - Then, finding the leftmost position where we can make a beneficial swap
- Finally, performing that single swap to get the maximum number
The greedy nature of choosing the leftmost position for swapping ensures we maximize the impact, as improving a more significant digit position yields a larger overall number.
Solution Approach
The implementation follows a greedy algorithm with preprocessing to efficiently find the optimal swap:
Step 1: Convert number to string
s = list(str(num))
n = len(s)
We convert the integer to a list of characters to easily access and swap individual digits.
Step 2: Build the maximum suffix array
d = list(range(n))
for i in range(n - 2, -1, -1):
if s[i] <= s[d[i + 1]]:
d[i] = d[i + 1]
We create an array d
where d[i]
stores the index of the maximum digit from position i
to the end of the string:
- Initialize
d[i] = i
for all positions (each position initially points to itself) - Traverse from right to left (from
n-2
to0
) - For each position
i
, compares[i]
withs[d[i+1]]
(the maximum digit to the right) - If
s[i] <= s[d[i+1]]
, updated[i] = d[i+1]
(the maximum to the right is larger or equal) - This ensures
d[i]
always points to the rightmost occurrence of the maximum digit from positioni
onwards
Step 3: Find and perform the optimal swap
for i, j in enumerate(d):
if s[i] < s[j]:
s[i], s[j] = s[j], s[i]
break
We scan from left to right to find the first position where swapping would increase the number:
- For each position
i
, check ifs[i] < s[d[i]]
- If true, swap
s[i]
withs[d[i]]
and break (we only need one swap) - This ensures we swap at the leftmost beneficial position for maximum impact
Step 4: Convert back to integer
return int(''.join(s))
Join the character list back into a string and convert to integer for the final result.
Time Complexity: O(n)
where n
is the number of digits - we make two passes through the digits.
Space Complexity: O(n)
for storing the string representation and the suffix maximum array d
.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the algorithm with num = 2736
:
Step 1: Convert to string
s = ['2', '7', '3', '6']
n = 4
Step 2: Build the maximum suffix array d
Initialize d = [0, 1, 2, 3]
(each position points to itself)
Now traverse from right to left:
-
i = 2
: Compares[2]='3'
withs[d[3]]='6'
- Since
'3' < '6'
, updated[2] = 3
d = [0, 1, 3, 3]
- Since
-
i = 1
: Compares[1]='7'
withs[d[2]]=s[3]='6'
- Since
'7' > '6'
, keepd[1] = 1
d = [0, 1, 3, 3]
- Since
-
i = 0
: Compares[0]='2'
withs[d[1]]=s[1]='7'
- Since
'2' < '7'
, updated[0] = 1
d = [1, 1, 3, 3]
- Since
The array d
now shows:
d[0] = 1
: From position 0 onwards, the max digit '7' is at index 1d[1] = 1
: From position 1 onwards, the max digit '7' is at index 1d[2] = 3
: From position 2 onwards, the max digit '6' is at index 3d[3] = 3
: From position 3 onwards, the max digit '6' is at index 3
Step 3: Find and perform the optimal swap
Scan from left to right:
i = 0
: Check ifs[0]='2' < s[d[0]]=s[1]='7'
- Yes!
'2' < '7'
, so swap positions 0 and 1 s = ['7', '2', '3', '6']
- Break (we're done)
- Yes!
Step 4: Convert back to integer
- Join:
'7236'
- Convert:
7236
The algorithm correctly identifies that swapping the leftmost digit '2' with the largest available digit '7' gives us the maximum possible value of 7236
.
Solution Implementation
1class Solution:
2 def maximumSwap(self, num: int) -> int:
3 # Convert number to list of digit characters for easy manipulation
4 digits = list(str(num))
5 n = len(digits)
6
7 # Create an array to store the index of the maximum digit to the right (including current position)
8 # max_right_index[i] stores the index of the maximum digit from position i to the end
9 max_right_index = list(range(n))
10
11 # Build the max_right_index array from right to left
12 # For each position, determine which index (current or the max to the right) has the larger digit
13 for i in range(n - 2, -1, -1):
14 # If current digit is less than or equal to the max digit on the right,
15 # inherit the index of the max digit on the right
16 if digits[i] <= digits[max_right_index[i + 1]]:
17 max_right_index[i] = max_right_index[i + 1]
18
19 # Find the leftmost position where we can make a beneficial swap
20 for i, j in enumerate(max_right_index):
21 # If the current digit is less than the maximum digit to its right,
22 # swap them to get the maximum possible number
23 if digits[i] < digits[j]:
24 digits[i], digits[j] = digits[j], digits[i]
25 break
26
27 # Convert the list of digit characters back to an integer
28 return int(''.join(digits))
29
1class Solution {
2 public int maximumSwap(int num) {
3 // Convert number to character array for digit manipulation
4 char[] digits = String.valueOf(num).toCharArray();
5 int length = digits.length;
6
7 // Array to store the index of the maximum digit to the right (including current position)
8 // maxIndexToRight[i] stores the index of the maximum digit from position i to the end
9 int[] maxIndexToRight = new int[length];
10
11 // Initialize each position with its own index
12 for (int i = 0; i < length; i++) {
13 maxIndexToRight[i] = i;
14 }
15
16 // Build the maxIndexToRight array from right to left
17 // For each position, store the index of the maximum digit from that position to the end
18 for (int i = length - 2; i >= 0; i--) {
19 // If current digit is less than or equal to the max digit on the right,
20 // update to point to the max digit's index
21 if (digits[i] <= digits[maxIndexToRight[i + 1]]) {
22 maxIndexToRight[i] = maxIndexToRight[i + 1];
23 }
24 }
25
26 // Find the first position where we can make a beneficial swap
27 for (int i = 0; i < length; i++) {
28 int maxIndex = maxIndexToRight[i];
29
30 // If current digit is smaller than the maximum digit to its right,
31 // swap them to maximize the number
32 if (digits[i] < digits[maxIndex]) {
33 // Perform the swap
34 char temp = digits[i];
35 digits[i] = digits[maxIndex];
36 digits[maxIndex] = temp;
37 break; // Only one swap is allowed
38 }
39 }
40
41 // Convert the character array back to integer and return
42 return Integer.parseInt(String.valueOf(digits));
43 }
44}
45
1class Solution {
2public:
3 int maximumSwap(int num) {
4 // Convert number to string for easier digit manipulation
5 string numStr = to_string(num);
6 int length = numStr.size();
7
8 // rightMaxIndex[i] stores the index of the maximum digit
9 // from position i to the end of the string
10 vector<int> rightMaxIndex(length);
11
12 // Initialize each position to point to itself
13 iota(rightMaxIndex.begin(), rightMaxIndex.end(), 0);
14
15 // Build the rightMaxIndex array from right to left
16 // For each position, determine the index of the maximum digit to its right (inclusive)
17 for (int i = length - 2; i >= 0; --i) {
18 // If current digit is less than or equal to the max digit on the right,
19 // update to point to that max digit's index
20 if (numStr[i] <= numStr[rightMaxIndex[i + 1]]) {
21 rightMaxIndex[i] = rightMaxIndex[i + 1];
22 }
23 }
24
25 // Find the first position where we can make a beneficial swap
26 for (int i = 0; i < length; ++i) {
27 int maxDigitIndex = rightMaxIndex[i];
28
29 // If there's a larger digit to the right, swap and return
30 if (numStr[i] < numStr[maxDigitIndex]) {
31 swap(numStr[i], numStr[maxDigitIndex]);
32 break;
33 }
34 }
35
36 // Convert the modified string back to integer
37 return stoi(numStr);
38 }
39};
40
1/**
2 * Finds the maximum number possible by swapping at most two digits
3 * @param num - The input number to maximize
4 * @returns The maximum number after at most one swap
5 */
6function maximumSwap(num: number): number {
7 // Convert number to array of digits (in reverse order)
8 const digits: number[] = [];
9 let tempNum = num;
10 while (tempNum !== 0) {
11 digits.push(tempNum % 10);
12 tempNum = Math.floor(tempNum / 10);
13 }
14
15 const digitCount = digits.length;
16
17 // Build array tracking the index of maximum digit from current position to the right
18 // maxIndexFromRight[i] stores the index of the largest digit from position i to 0
19 const maxIndexFromRight: number[] = [];
20 for (let i = 0, maxIndex = 0; i < digitCount; i++) {
21 if (digits[i] > digits[maxIndex]) {
22 maxIndex = i;
23 }
24 maxIndexFromRight.push(maxIndex);
25 }
26
27 // Traverse digits from left to right (most significant to least significant)
28 // Find the first position where we can swap with a larger digit from the right
29 for (let i = digitCount - 1; i >= 0; i--) {
30 if (digits[maxIndexFromRight[i]] !== digits[i]) {
31 // Swap current digit with the maximum digit found to its right
32 [digits[maxIndexFromRight[i]], digits[i]] = [digits[i], digits[maxIndexFromRight[i]]];
33 break;
34 }
35 }
36
37 // Reconstruct the number from the digit array
38 let result = 0;
39 for (let i = digitCount - 1; i >= 0; i--) {
40 result = result * 10 + digits[i];
41 }
42
43 return result;
44}
45
Time and Space Complexity
The time complexity is O(log M)
, where M
is the value of the input number num
. This is because:
- Converting the number to a string takes
O(log M)
time (the number of digits innum
is proportional tolog M
) - The first loop iterates through all digits from right to left, which is
O(n)
wheren
is the number of digits, andn = O(log M)
- The second loop also iterates through the digits at most once, which is
O(n) = O(log M)
- Converting back to an integer takes
O(log M)
time
The space complexity is O(log M)
because:
- The string representation
s
stores all digits of the number, requiringO(log M)
space - The auxiliary array
d
also stores indices for each digit position, requiringO(log M)
space - The total space used is proportional to the number of digits in
num
, which isO(log M)
Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Not Finding the Rightmost Occurrence of Maximum Digit
Issue: A common mistake is swapping with the first occurrence of the maximum digit to the right, rather than the rightmost occurrence. This can lead to suboptimal results.
Example: For num = 1993
, if we swap position 0 ('1') with the first '9' at position 1, we get 9193
. However, swapping with the rightmost '9' at position 2 gives us 9913
, which is larger.
Why it happens: When building the suffix maximum array, using <
instead of <=
in the comparison will point to the first occurrence rather than the last.
# Incorrect - points to first occurrence of max
for i in range(n - 2, -1, -1):
if s[i] < s[d[i + 1]]: # Wrong comparison
d[i] = d[i + 1]
# Correct - points to rightmost occurrence of max
for i in range(n - 2, -1, -1):
if s[i] <= s[d[i + 1]]: # Correct comparison
d[i] = d[i + 1]
Pitfall 2: Swapping Multiple Times
Issue: Performing multiple swaps instead of at most one swap, which violates the problem constraint.
Example: For num = 2736
, after swapping to get 7236
, continuing to look for more swaps would be incorrect.
Solution: Use break
immediately after the first swap:
for i, j in enumerate(d):
if s[i] < s[j]:
s[i], s[j] = s[j], s[i]
break # Critical - stop after first swap
Pitfall 3: Incorrect Handling of Already-Maximum Numbers
Issue: Attempting to force a swap when the number is already at its maximum value.
Example: For num = 9973
, the number is already optimal, but incorrect logic might try to swap equal digits.
Why it works correctly: The condition s[i] < s[j]
ensures we only swap when there's an actual improvement. If the number is already maximum, this condition will never be true, and no swap occurs.
Pitfall 4: String Concatenation Instead of List Manipulation
Issue: Using string concatenation for swapping, which is inefficient and error-prone in Python since strings are immutable.
# Inefficient and error-prone
s = str(num)
s = s[:i] + s[j] + s[i+1:j] + s[i] + s[j+1:] # Complex and bug-prone
# Better approach - use list
s = list(str(num))
s[i], s[j] = s[j], s[i] # Simple and clear
Pitfall 5: Not Considering Single-Digit Numbers
Issue: Edge case handling for single-digit numbers like num = 7
.
Solution: The algorithm naturally handles this case since:
- A single-digit number has
n = 1
- The loop
for i in range(n - 2, -1, -1)
doesn't execute (range is empty) - The suffix array remains
d = [0]
- The swap condition
s[0] < s[0]
is false, so no swap occurs - The number is returned unchanged, which is correct
What's the output of running the following function using input 56
?
1KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12def letter_combinations_of_phone_number(digits):
13 def dfs(path, res):
14 if len(path) == len(digits):
15 res.append(''.join(path))
16 return
17
18 next_number = digits[len(path)]
19 for letter in KEYBOARD[next_number]:
20 path.append(letter)
21 dfs(path, res)
22 path.pop()
23
24 res = []
25 dfs([], res)
26 return res
27
1private static final Map<Character, char[]> KEYBOARD = Map.of(
2 '2', "abc".toCharArray(),
3 '3', "def".toCharArray(),
4 '4', "ghi".toCharArray(),
5 '5', "jkl".toCharArray(),
6 '6', "mno".toCharArray(),
7 '7', "pqrs".toCharArray(),
8 '8', "tuv".toCharArray(),
9 '9', "wxyz".toCharArray()
10);
11
12public static List<String> letterCombinationsOfPhoneNumber(String digits) {
13 List<String> res = new ArrayList<>();
14 dfs(new StringBuilder(), res, digits.toCharArray());
15 return res;
16}
17
18private static void dfs(StringBuilder path, List<String> res, char[] digits) {
19 if (path.length() == digits.length) {
20 res.add(path.toString());
21 return;
22 }
23 char next_digit = digits[path.length()];
24 for (char letter : KEYBOARD.get(next_digit)) {
25 path.append(letter);
26 dfs(path, res, digits);
27 path.deleteCharAt(path.length() - 1);
28 }
29}
30
1const KEYBOARD = {
2 '2': 'abc',
3 '3': 'def',
4 '4': 'ghi',
5 '5': 'jkl',
6 '6': 'mno',
7 '7': 'pqrs',
8 '8': 'tuv',
9 '9': 'wxyz',
10}
11
12function letter_combinations_of_phone_number(digits) {
13 let res = [];
14 dfs(digits, [], res);
15 return res;
16}
17
18function dfs(digits, path, res) {
19 if (path.length === digits.length) {
20 res.push(path.join(''));
21 return;
22 }
23 let next_number = digits.charAt(path.length);
24 for (let letter of KEYBOARD[next_number]) {
25 path.push(letter);
26 dfs(digits, path, res);
27 path.pop();
28 }
29}
30
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
Math for Technical Interviews How much math do I need to know for technical interviews The short answer is about high school level math Computer science is often associated with math and some universities even place their computer science department under the math faculty However the reality is that you
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
Want a Structured Path to Master System Design Too? Don’t Miss This!