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; } |
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)); |
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 neveropen!