Skip to content
Related Articles
Open in App
Not now

Related Articles

Lexicographical smallest number after at most K consecutive swaps

Improve Article
Save Article
Like Article
  • Difficulty Level : Expert
  • Last Updated : 16 Mar, 2023
Improve Article
Save Article
Like Article

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 <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;
}


Java




import java.util.*;
 
class GFG {
 
  // Function to mark the pos as 1 that
  // are shifted in string in Segtree node
  public static void SegAdd(int[] seg, int start, int end, int pos, int curr) {
 
    // Out of Range
    if (pos < start || pos > end) {
      return;
    }
 
    // Check if we reach the position
    if (start == end) {
      seg[curr] = 1;
      return;
    }
    // Initialize the mid
    int mid = (start + end) / 2;
    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];
  }
 
  // Function to find the number of
  // elements which got shifted
  public static int SegFind(int[] seg, int pos_start, int pos_end, int start, int end, int curr) {
    if (pos_end < start || pos_start > end) {
      return 0;
    }
 
    // Return 0 is the end position is
    // less than start or the start
    // position is greater than end
    if (pos_start <= start && pos_end >= end) {
      return seg[curr];
    }
    int mid = (start + end) / 2;
    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 left + right;
  }
 
  // Function to remove leading zeros
  // from the given string str
  public static String RemoveLeadingZeros(String s) {
    int i = 0;
    String ans = "";
    while (i < s.length()) {
      if (s.charAt(i) == '0') {
        i += 1;
      } else {
        break;
      }
    }
    ans = s.substring(i);
    return ans;
  }
 
  // Function to find the lexicographically
  // smallest integer
  public static String FindMinimumInteger(String s, int k) {
    s = RemoveLeadingZeros(s);
    int n = s.length();
    int[] seg = new int[2 * (int) Math.pow(2, Math.ceil(Math.log(n) / Math.log(2))) - 1];
    Map<Integer, List<Integer>> m = new HashMap<Integer, List<Integer>>();
    for (int i = 0; i < 10; i++) {
      m.put(i, new ArrayList<Integer>());
    }
    for (int i = 0; i < n; i++) {
      m.get(Integer.parseInt(String.valueOf(s.charAt(i)))).add(i);
    }
    String res = "";
    for (int i = 0; i < n; i++) {
      for (int digit = 0; digit < 10; digit++) {
        if (m.get(digit).size() != 0) {
          int original_pos = m.get(digit).get(0);
          int shift = SegFind(seg, original_pos, n - 1, 0, n - 1, 0);
          int new_pos = original_pos + shift - i;
          if (new_pos <= k) {
            k -= new_pos;
            SegAdd(seg, 0, n - 1, original_pos, 0);
            res += String.valueOf(digit);
            m.get(digit).remove(0);
            break;
          }
        }
      }
    }
    return res;
  }
 
  // Driver code
  public static void main(String[] args) {
    String s = "9438957234785635408";
    int k = 23;
    System.out.println(FindMinimumInteger(s, k));
  }
}


Python3




import math
 
# Function to mark the pos as 1 that
# are shifted in string in Segtree node
def segAdd(seg, start, end, pos, 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
    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
def segFind(seg, pos_start, pos_end, start, end, 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
    mid = (start + end) // 2
    # Recursion call
    left = segFind(seg, pos_start, pos_end, start, mid, 2 * curr + 1)
    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
def removeLeadingZeros(s):
    i = 0
    # To store the resultant string
    ans = ""
    while i < len(s):
        # If Initial character is 0,
        # then remove it
        if s[i] == '0':
            i += 1
        # Else break
        else:
            break
    ans = s[i:]
    # Return the updated string
    return ans
 
# Function to find the lexicographically
# smallest integer
def findMinimumInteger(s, k):
    # To remove leading zeros
    s = removeLeadingZeros(s)
    n = len(s)
    # Segment tree vector
    seg = [0] * (2 * (2 ** math.ceil(math.log2(n))) - 1)
    # Hash to find the original
    # position of the digit
    m = {digit:[] for digit in range(10)}
    for i in range(n):
        m[int(s[i])].append(i)
    # Resultant string variable
    res = ""
    for i in range(n):
        # Place a digit from
        # 0-9 which fit best
        for digit in range(10):
            if len(m[digit]) != 0:
                original_pos = m[digit][0]
                # Find the number of
                # right elements that
                # are shifted from
                # current element
                shift = segFind(seg, original_pos, n - 1, 0, n - 1, 0)
                # Mark the new position
                new_pos = original_pos + shift - i
                if new_pos <= k:
                    k -= new_pos
                    # Add the original position
                    # Add the original position of
                    # the element which got shifted
                    segAdd(seg, 0, n - 1,original_pos, 0);
 
                    res += str(digit);
                    m[digit].pop(0);
                    break;
                 
 
    # Return the result
    return res;
 
# Driver Code
 
# Given string
s = "9438957234785635408";
 
# Given K swaps
k = 23;
 
# Function Call
print(findMinimumInteger(s, k))


C#




using System;
using System.Collections.Generic;
 
class GFG {
   
  // Function to mark the pos as 1 that
  // are shifted in string in Segtree node
  public static void SegAdd(int[] seg, int start, int end,
                            int pos, int curr)
  {
 
    // Out of Range
    if (pos < start || pos > end) {
      return;
    }
 
    // Check if we reach the position
    if (start == end) {
      seg[curr] = 1;
      return;
    }
 
    // Initialize the mid
    int mid = (start + end) / 2;
    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];
  }
 
  // Function to find the number of
  // elements which got shifted
  public static int SegFind(int[] seg, int pos_start,
                            int pos_end, int start,
                            int end, int curr)
  {
    if (pos_end < start || pos_start > end) {
      return 0;
    }
 
    // Return 0 is the end position is
    // less than start or the start
    // position is greater than end
    if (pos_start <= start && pos_end >= end) {
      return seg[curr];
    }
    int mid = (start + end) / 2;
    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 left + right;
  }
 
  // Function to remove leading zeros
  // from the given string str
  public static string RemoveLeadingZeros(string s)
  {
    int i = 0;
    string ans = "";
    while (i < s.Length) {
      if (s[i] == '0') {
        i += 1;
      }
      else {
        break;
      }
    }
    ans = s.Substring(i);
    return ans;
  }
 
  // Function to find the lexicographically
  // smallest integer
  public static string FindMinimumInteger(string s, int k)
  {
    s = RemoveLeadingZeros(s);
    int n = s.Length;
    int[] seg
      = new int[2
                * (int)Math.Pow(
                  2,
                  Math.Ceiling(Math.Log(n, 2)))
                - 1];
    Dictionary<int, List<int> > m
      = new Dictionary<int, List<int> >();
    for (int i = 0; i < 10; i++) {
      m[i] = new List<int>();
    }
    for (int i = 0; i < n; i++) {
      m[int.Parse(s[i].ToString())].Add(i);
    }
    string res = "";
    for (int i = 0; i < n; i++) {
      for (int digit = 0; digit < 10; digit++) {
        if (m[digit].Count != 0) {
          int original_pos = m[digit][0];
          int shift = SegFind(seg, original_pos,
                              n - 1, 0, n - 1, 0);
          int new_pos = original_pos + shift - i;
          if (new_pos <= k) {
            k -= new_pos;
            SegAdd(seg, 0, n - 1, original_pos,
                   0);
            res += Convert.ToString(digit);
            m[digit].RemoveAt(0);
            break;
          }
        }
      }
    }
    return res;
  }
 
  // Driver code
  public static void Main(string[] args)
  {
    string s = "9438957234785635408";
    int k = 23;
 
    // Function call
    Console.WriteLine(FindMinimumInteger(s, k));
  }
}


Javascript




// Function to mark the pos as 1 that
// are shifted in string in Segtree node
function segAdd(seg, start, end, pos, curr) {
 
  // Out of Range
  if (pos < start || pos > end) {
    return;
  }
   
  // Check if we reach the position
  if (start == end) {
    seg[curr] = 1;
    return;
  }
   
  // Initialize the mid
  const mid = Math.floor((start + end) / 2);
  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];
}
 
// Function to find the number of
// elements which got shifted
function segFind(seg, pos_start, pos_end, start, end, curr) {
  if (pos_end < start || pos_start > end) {
    return 0;
  }
   
  // Return 0 is the end position is
  // less than start or the start
  // position is greater than end
  if (pos_start <= start && pos_end >= end) {
    return seg[curr];
  }
  const mid = Math.floor((start + end) / 2);
  const left = segFind(seg, pos_start, pos_end, start, mid, 2 * curr + 1);
  const right = segFind(seg, pos_start, pos_end, mid + 1, end, 2 * curr + 2);
  return left + right;
}
 
// Function to remove leading zeros
// from the given string str
function removeLeadingZeros(s) {
  let i = 0;
  let ans = "";
  while (i < s.length) {
    if (s[i] == "0") {
      i += 1;
    } else {
      break;
    }
  }
  ans = s.substring(i);
  return ans;
}
 
// Function to find the lexicographically
// smallest integer
function findMinimumInteger(s, k) {
  s = removeLeadingZeros(s);
  const n = s.length;
  const seg = new Array(2 * 2 ** Math.ceil(Math.log2(n)) - 1).fill(0);
  const m = {};
  for (let i = 0; i < 10; i++) {
    m[i] = [];
  }
  for (let i = 0; i < n; i++) {
    m[parseInt(s[i])].push(i);
  }
  let res = "";
  for (let i = 0; i < n; i++) {
    for (let digit = 0; digit < 10; digit++) {
      if (m[digit].length != 0) {
        const original_pos = m[digit][0];
        const shift = segFind(seg, original_pos, n - 1, 0, n - 1, 0);
        const new_pos = original_pos + shift - i;
        if (new_pos <= k) {
          k -= new_pos;
          segAdd(seg, 0, n - 1, original_pos, 0);
          res += digit.toString();
          m[digit].shift();
          break;
        }
      }
    }
  }
  return res;
}
 
// Driver code
 
const s = "9438957234785635408";
const k = 23;
 
console.log(findMinimumInteger(s, k));


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
Like Article
Save Article
Related Articles

Start Your Coding Journey Now!