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 nodevoid 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 shiftedint 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 strstring 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 integerstring 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 Codeint 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 nodedef 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 shifteddef 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 strdef 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 integerdef 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 strings = "9438957234785635408";# Given K swapsk = 23;# Function Callprint(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 nodefunction 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 shiftedfunction 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 strfunction 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 integerfunction 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 codeconst s = "9438957234785635408";const k = 23;console.log(findMinimumInteger(s, k)); |
0345989723478563548
Time Complexity: O(N * log N), where N is the length of the string.
Auxiliary Space: O(N)
Ready to dive in? Explore our Free Demo Content and join our DSA course, trusted by over 100,000 zambiatek!



