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:

  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++

filter_none

edit
close

play_arrow

link
brightness_4
code

// 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 positon
    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 postion 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 rigt = segFind(
        seg, pos_start, pos_end,
        mid + 1, end, 2 * curr + 2);
  
    // Return the result
    return left + rigt;
}
  
// 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 lexiographically
// 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 postion 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;
}

chevron_right


Output:

0345989723478563548

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

competitive-programming-img




My Personal Notes arrow_drop_up

Competitive Programming enthusiast 4 star at codechef 5 star at Hackerrank love to solve problems

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.