Lexicographical smallest number after at most K consecutive swaps
Given a number in form of string str and an integer K, the task is to find the smallest integer that can be formed after performing at most K consecutive swaps.
The consecutive swaps mean at one swap the character at index i can be swapped with character at index i – 1 or i + 1.
Examples:
Input: str = “76921”, K = 3
Output: 27691
Explanation:
27691 is the lexicographical smallest possible number.Input: str = “9438957234785635408”, K = 23
Output: 0345989723478563548
Explanation:
0345989723478563548 is the lexicographical smallest possible number.
Naive Approach: The simplest idea is to generate all possible permutation of the given string and check whether which lexicographically smallest string satisfies the conditions for at most K swaps. Print that string.
Time Complexity: O(N!), where N is the length of the given string.
Auxiliary Space: O(1)
Better Approach: A better approach is to use Greedy Approach. Below are the steps:
- Remove the leading zero if exists in the given number.
- Pick the smallest element from the string str [Consider str[k] when K is smaller, else N].
- Place the smallest element to the 0th position after shifting all these elements by 1 position right.
- Subtract the number of swaps in the above steps from K.
- If still we are left with K > 0 then we apply the same procedure from the very next starting position i.e., str[2, …N], and then place it to the 1st position.
- So we keep applying the same process until K becomes 0.
Time Complexity: O(N2), where N is the length of the given string.
Auxiliary Space: O(1)
Efficient Approach: The idea is to use the Segment tree and Hashing. Below are the steps:
- Remove the leading zero if exists in the given number.
- Store the original position of the digits in a Map and find which digit the best fits at every index whose shifting will be less than equal to K.
- Use a Map to find the original position of the digit.
- Find the number of digits that are right to the current position and that is shifted, for this, mark the position of which are shifted in a segment tree from [0 … N-1].
- Every node of the segment tree contains the number of positions that got shifted. Now the task is to find how many positions got shifted in the range [current_index, N-1] because only that will affect the original position.
- The new position to shift will be (original_position + number_of_right – element_shifted – i), that is the original position of the element is added to the segment tree which just got shifted.
Below is the program for the above approach:
C++
// C++ program for the above approach #include <bits/stdc++.h> using namespace std; // Function to mark the pos as 1 that // are shifted in string in Segtree node void segAdd(vector< int >& seg, int start, int end, int pos, int curr) { // Out of Range if (pos < start or pos > end) return ; // Check if we reach the position if (start == end) { seg[curr] = 1; return ; } // Initialize the mid int mid = (start + end) / 2; // Recursion call segAdd(seg, start, mid, pos, 2 * curr + 1); segAdd(seg, mid + 1, end, pos, 2 * curr + 2); // Every node contains no of // marked position that are // shifted in a range seg[curr] = seg[2 * curr + 1] + seg[2 * curr + 2]; return ; } // Function to find the number of // elements which got shifted int segFind( vector< int >& seg, int pos_start, int pos_end, int start, int end, int curr) { // Return 0 is the end position is // less than start or the start // position is greater than end if (pos_end < start or pos_start > end) return 0; if (pos_start <= start and pos_end >= end) return seg[curr]; // Initialize the mid int mid = (start + end) / 2; // Recursion call int left = segFind( seg, pos_start, pos_end, start, mid, 2 * curr + 1); int right = segFind( seg, pos_start, pos_end, mid + 1, end, 2 * curr + 2); // Return the result return left + right; } // Function to remove leading zeros // from the given string str string removeLeadingZeros(string str) { int i = 0; // To store the resultant string string ans = "" ; for (; i < str[i]; i++) { // If Initial character is 0, // then remove it if (str[i] == '0' ) { i++; } // Else break else { break ; } } ans = str.substr(i - 1, str.length()); // Return the updated string return ans; } // Function to find the lexicographically // smallest integer string findMinimumInteger( string arr, int k) { // To remove leading zeros arr = removeLeadingZeros(arr); int n = arr.size(); // Segment tree vector vector< int > seg( (2 * ( int ) pow ( 2, ( int )( ceil (log2(n)))) - 1), 0); // Hash to find the original // position of the digit unordered_map< int , list< int > > m; for ( int i = 0; i < n; i++) { m[arr[i] - '0' ].push_back(i); } // Resultant string variable string res = "" ; for ( int i = 0; i < n; i++) { // Place a digit from // 0-9 which fit best for ( int digit = 0; digit <= 9; digit++) { if (m[digit].size() != 0) { int original_pos = m[digit].front(); // Find the number of // right elements that // are shifted from // current element int shift = segFind( seg, original_pos, n - 1, 0, n - 1, 0); // Mark the new position int new_pos = original_pos + shift - i; if (new_pos <= k) { k -= new_pos; // Add the original position of // the element which got shifted segAdd(seg, 0, n - 1, original_pos, 0); res.push_back( '0' + digit); m[digit].pop_front(); break ; } } } } // Return the result return res; } // Driver Code int main() { // Given string string s = "9438957234785635408" ; // Given K swaps int k = 23; // Function Call cout << findMinimumInteger(s, k) << endl; } |
0345989723478563548
Time Complexity: O(N * log N), where N is the length of the string.
Auxiliary Space: O(N)
Please Login to comment...