Lexicographical smallest number after at most K consecutive swaps

• Difficulty Level : Expert
• Last Updated : 14 Sep, 2021

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:

1. Remove the leading zero if exists in the given number.
2. Pick the smallest element from the string str [Consider str[k] when K is smaller, else N].
3. Place the smallest element to the 0th position after shifting all these elements by 1 position right.
4. Subtract the number of swaps in the above steps from K.
5. 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.
6. 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:

1. Remove the leading zero if exists in the given number.
2. 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.
3. Use a Map to find the original position of the digit.
4. 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].
5. 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.
6. 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 using namespace std;   // Function to mark the pos as 1 that // are shifted in string in Segtree node void segAdd(vector& 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& 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 seg(         (2 * (int)pow(                  2,                  (int)(ceil(log2(n))))          - 1),         0);       // Hash to find the original     // position of the digit     unordered_map > 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; }

Output:

0345989723478563548

Time Complexity: O(N * log N), where N is the length of the string.
Auxiliary Space: O(N)

My Personal Notes arrow_drop_up
Recommended Articles
Page :