Minimum Adjacent Swaps to Reach the Kth Smallest Number
MedPrompt
You are given a string num, representing a large integer, and an integer k.
We call some integer wonderful if it is a permutation of the digits in num and is greater in value than num. There can be many wonderful integers. However, we only care about the smallest-valued ones.
- For example, when
num = "5489355142":- The 1st smallest wonderful integer is
"5489355214". - The 2nd smallest wonderful integer is
"5489355241". - The 3rd smallest wonderful integer is
"5489355412". - The 4th smallest wonderful integer is
"5489355421".
- The 1st smallest wonderful integer is
Return the minimum number of adjacent digit swaps that needs to be applied to num to reach the kth smallest wonderful integer.
The tests are generated in such a way that kth smallest wonderful integer exists.
Example 1:
Input: num = "5489355142", k = 4
Output: 2
Explanation: The 4th smallest wonderful number is "5489355421". To get this number:
- Swap index 7 with index 8: "5489355142" -> "5489355412"
- Swap index 8 with index 9: "5489355412" -> "5489355421"Example 2:
Input: num = "11112", k = 4
Output: 4
Explanation: The 4th smallest wonderful number is "21111". To get this number:
- Swap index 3 with index 4: "11112" -> "11121"
- Swap index 2 with index 3: "11121" -> "11211"
- Swap index 1 with index 2: "11211" -> "12111"
- Swap index 0 with index 1: "12111" -> "21111"Example 3:
Input: num = "00123", k = 1
Output: 1
Explanation: The 1st smallest wonderful number is "00132". To get this number:
- Swap index 3 with index 4: "00123" -> "00132"
Constraints:
2 <= num.length <= 10001 <= k <= 1000numonly consists of digits.
Approaches
2 approaches with complexity analysis and trade-offs.
This approach first determines the target k-th smallest wonderful number by repeatedly applying the "next permutation" algorithm k times. Then, it calculates the minimum adjacent swaps required to transform the original number string into the target string. This is done by greedily placing the correct digit at each position from left to right, simulating the swaps and counting them.
Algorithm
- Step 1: Find the k-th smallest wonderful number.
- A "wonderful number" is a permutation of the digits of
numthat is larger thannum. The smallest wonderful number is the next lexicographical permutation. - To find the
k-th smallest, start withnumand apply the standardnext_permutationalgorithmktimes.
- A "wonderful number" is a permutation of the digits of
- Step 2: Calculate minimum adjacent swaps via simulation.
- Convert the original
numstring to a mutable list of characters,originalList. - The result from Step 1 is the
targetstring. - Initialize
swaps = 0. - Iterate from
i = 0ton-1(wherenis the number of digits):- If the character at
originalList.get(i)does not matchtarget[i], find the first indexj > iwhereoriginalList.get(j)equalstarget[i]. - Move the character from index
jtoi. This can be done by removing the character atjand inserting it ati. - This operation is equivalent to
j - iadjacent swaps. Add this number to the totalswapscount.
- If the character at
- Convert the original
- Step 3: Return total swaps.
- After the loop finishes,
swapswill hold the minimum number of adjacent swaps required.
- After the loop finishes,
Walkthrough
The solution is broken down into two main parts:
-
Finding the Target Permutation: The problem defines a "wonderful" integer as a permutation of
num's digits that is greater thannum. Thek-th smallest wonderful integer is simply thek-th lexicographically next permutation ofnum. We can find this by implementing the standardnext_permutationalgorithm and applying itktimes to the digits ofnum.private void nextPermutation(char[] arr) { int n = arr.length; int i = n - 2; while (i >= 0 && arr[i] >= arr[i + 1]) { i--; } if (i >= 0) { int j = n - 1; while (arr[j] <= arr[i]) { j--; } swap(arr, i, j); } reverse(arr, i + 1, n - 1);} private void swap(char[] arr, int i, int j) { /* ... */ }private void reverse(char[] arr, int start, int end) { /* ... */ } -
Greedy Swap Calculation: Once we have the target string, say
target, we need to find the minimum adjacent swaps to changenumtotarget. A greedy approach works here. We iterate from left to right. For each positioni, we ensure the correct charactertarget[i]is placed there. We find the first occurrence oftarget[i]in our evolving string (starting from indexi), say at indexj, and then performj-iadjacent swaps to move it to positioni. We sum up these swaps. Using aListsimplifies the removal and insertion of characters.public int getMinSwaps(String num, int k) { char[] targetArr = num.toCharArray(); for (int i = 0; i < k; i++) { nextPermutation(targetArr); } List<Character> originalList = new ArrayList<>(); for (char c : num.toCharArray()) { originalList.add(c); } int n = num.length(); int swaps = 0; for (int i = 0; i < n; i++) { if (originalList.get(i) != targetArr[i]) { int j = i + 1; while (originalList.get(j) != targetArr[i]) { j++; } char charToMove = originalList.get(j); originalList.remove(j); originalList.add(i, charToMove); swaps += (j - i); } } return swaps;}
Complexity
Time
O(k*N + N^2). Finding the target number takes O(k*N). The greedy swap calculation involves an outer loop of N iterations. Inside, finding the character and shifting elements (or using `List.remove`/`add`) takes O(N) time, leading to an O(N^2) complexity for the second part.
Space
O(N), where N is the length of `num`. This space is used to store the character arrays or lists for the original and target numbers.
Trade-offs
Pros
The logic is intuitive and directly simulates the process of swapping digits.
It's relatively easy to implement without requiring advanced data structures.
Cons
The O(N^2) complexity for calculating swaps can be inefficient for larger values of N (though it passes for the given constraints).
Repeatedly modifying a list by removing and inserting elements can be less performant than array-based manipulations, but conceptually simpler.
This more efficient approach also begins by finding the k-th smallest wonderful number. However, it calculates the minimum swaps using a more advanced technique. The problem is transformed into counting inversions in a permutation mapping. This mapping indicates the destination index for each digit from the original number. By using a Fenwick Tree (also known as a Binary Indexed Tree), we can count these inversions in O(N log N) time, which is faster than the O(N^2) simulation.
Algorithm
- Step 1: Find the k-th smallest wonderful number.
- This step is identical to the previous approach. Apply the
next_permutationalgorithmktimes to find thetargetstring.
- This step is identical to the previous approach. Apply the
- Step 2: Map original positions to target positions.
- The minimum number of adjacent swaps to transform a string
Ato its permutationBis the number of inversions in the mapping of element positions. To handle duplicate digits correctly, we must preserve their relative order. - For each digit '0'-'9', create a queue of its indices in the
targetstring. - Create a permutation array
Pof sizeN. Iterate through the originalnumstring fromi = 0toN-1. For each digitnum[i], get its corresponding target position by dequeuing from the appropriate digit's queue. Store this position inP[i].
- The minimum number of adjacent swaps to transform a string
- Step 3: Count inversions in P using a Fenwick Tree (BIT).
- An inversion in
Pis a pair(i, j)wherei < jandP[i] > P[j]. - Initialize a Fenwick Tree of size
N+1. - Initialize
inversions = 0. - Iterate through
Pfromi = 0toN-1:- For each
P[i], query the BIT to find how many numbers greater thanP[i]have already been processed. This count is the number of new inversions involvingP[i]. - Add this count to
inversions. - Update the BIT to mark
P[i]as seen.
- For each
- An inversion in
- Step 4: Return total inversions.
Walkthrough
This method optimizes the calculation of swaps.
-
Finding the Target Permutation: Same as the first approach, taking
O(k*N)time. -
Calculating Swaps via Inversion Count: The key insight is that the minimum number of adjacent swaps required to transform one permutation into another is equal to the number of inversions in their position mapping. An inversion is a pair of elements that are in the wrong order relative to each other.
-
Constructing the Permutation Map
P: We must map each character innumto its final position intarget. To handle duplicates and ensure minimum swaps, the relative order of identical characters must be maintained (e.g., the first '5' innummaps to the first '5' intarget). This mapping can be built inO(N)time using queues for each digit's target indices. -
Counting Inversions with a Fenwick Tree: With the permutation map
P, we count its inversions. A Fenwick Tree is perfect for this. We iterate throughP, and for each elementP[i], we query the tree for how many elementsP[j]withj < iare greater thanP[i]. This is anO(log N)operation. We then update the tree withP[i], alsoO(log N). Repeating for allNelements gives a total time ofO(N log N).
public int getMinSwaps(String num, int k) { char[] targetArr = num.toCharArray(); for (int i = 0; i < k; i++) { nextPermutation(targetArr); } String target = new String(targetArr); int n = num.length(); // Create mapping from original index to target index List<Integer>[] pos = new List[10]; for (int i = 0; i < 10; i++) pos[i] = new ArrayList<>(); for (int i = 0; i < n; i++) { pos[target.charAt(i) - '0'].add(i); } int[] p = new int[n]; int[] indexCounters = new int[10]; for (int i = 0; i < n; i++) { int digit = num.charAt(i) - '0'; p[i] = pos[digit].get(indexCounters[digit]++); } // Count inversions in p using Fenwick Tree FenwickTree ft = new FenwickTree(n); int inversions = 0; for (int i = 0; i < n; i++) { inversions += ft.queryRange(p[i] + 2, n); // Count elements > p[i] already seen ft.update(p[i] + 1, 1); // Mark p[i] as seen } return inversions;} class FenwickTree { int[] bit; int size; FenwickTree(int n) { this.size = n + 1; this.bit = new int[this.size]; } void update(int index, int val) { /* ... */ } int query(int index) { /* ... */ } int queryRange(int l, int r) { if (l > r) return 0; return query(r) - query(l - 1); }} -
Complexity
Time
O(k*N + N log N). Finding the target is O(k*N). Building the map `P` is O(N). Counting inversions with a Fenwick Tree is O(N log N). The overall complexity is determined by the larger of O(k*N) and O(N log N).
Space
O(N), where N is the length of `num`. Space is needed for the target string, position lists (`O(N)` total), the permutation map `P` (`O(N)`), and the Fenwick Tree (`O(N)`).
Trade-offs
Pros
Asymptotically more efficient for the swap calculation part (O(N log N) vs O(N^2)).
It is a standard and robust technique for problems involving permutation distance.
Cons
Requires knowledge of advanced data structures like Fenwick Trees.
The implementation is more complex than the direct simulation approach.
Solutions
Solution
class Solution {public int getMinSwaps(String num, int k) { char[] s = num.toCharArray(); for (int i = 0; i < k; ++i) { nextPermutation(s); } List<Integer>[] d = new List[10]; Arrays.setAll(d, i->new ArrayList<>()); int n = s.length; for (int i = 0; i < n; ++i) { d[num.charAt(i) - '0'].add(i); } int[] idx = new int[10]; int[] arr = new int[n]; for (int i = 0; i < n; ++i) { arr[i] = d[s[i] - '0'].get(idx[s[i] - '0']++); } int ans = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { if (arr[j] > arr[i]) { ++ans; } } } return ans; }private boolean nextPermutation(char[] nums) { int n = nums.length; int i = n - 2; while (i >= 0 && nums[i] >= nums[i + 1]) { --i; } if (i < 0) { return false; } int j = n - 1; while (j >= 0 && nums[i] >= nums[j]) { --j; } swap(nums, i++, j); for (j = n - 1; i < j; ++i, --j) { swap(nums, i, j); } return true; }private void swap(char[] nums, int i, int j) { char t = nums[i]; nums[i] = nums[j]; nums[j] = t; }}class Solution {public: int getMinSwaps(string num, int k) { string s = num; for (int i = 0; i < k; ++i) { next_permutation(begin(s), end(num)); } vector<int> d[10]; int n = num.size(); for (int i = 0; i < n; ++i) { d[num[i] - '0'].push_back(i); } int idx[10]{}; vector<int> arr(n); for (int i = 0; i < n; ++i) { arr[i] = d[s[i] - '0'][idx[s[i] - '0']++]; } int ans = 0; for (int i = 0; i < n; ++i) { for (int j = 0; j < i; ++j) { if (arr[j] > arr[i]) { ++ans; } } } return ans; }};class Solution: def getMinSwaps(self, num: str, k: int) -> int: def next_permutation(nums: List[str]) -> bool: n = len(nums) i = n - 2 while i >= 0 and nums[i] >= nums[i + 1]: i -= 1 if i < 0: return False j = n - 1 while j >= 0 and nums[j] <= nums[i]: j -= 1 nums[i], nums[j] = nums[j], nums[i] nums[i + 1: n] = nums[i + 1: n][:: - 1] return True s = list(num) for _ in range(k): next_permutation(s) d = [[] for _ in range(10)] idx = [0] * 10 n = len(s) for i, c in enumerate(num): j = ord(c) - ord("0") d[j]. append(i) arr = [0] * n for i, c in enumerate(s): j = ord(c) - ord("0") arr[i] = d[j][idx[j]] idx[j] += 1 return sum(arr[j] > arr[i] for i in range(n) for j in range(i))Video walkthrough
Newsletter
One sharp idea, every week
System design and interview prep — short enough to finish.
No spam. Unsubscribe anytime.
Practice
Same difficulty — related problems to reinforce the pattern.