Given a string s consisting of only lowercase characters, the task is to find the lexicographically smallest string after removing exactly k characters from the string. But you have to modify the value of k, i.e., if the length of the string is a power of 2, reduce k by half, else multiply k by 2. You can remove any k character.
Note: If it is not possible to remove k (the value of k after correction) characters or if the resulting string is empty return -1.
Examples:
Input: s = "fooland", k = 2
Output: "and"Â
Explanation: As the size of the string = 7, which is not a power of 2, hence k = 4. After removing 4 characters from the given string, the lexicographically smallest string is "and".Input: s = "code", k = 4
Output: "cd"
Explanation: As the length of the string = 4, which is 2 to the power 2, hence k = 2. Hence, lexicographically smallest string after removal of 2 characters is "cd".
Table of Content
[Naive Approach] Using Nested Loops - O(n^2) time and O(n) space
The idea is to find the smallest (n - k) characters from string using nested loop.
Check if Length is Power of 2: First, check if the string's length is a power of 2. If it is, divide k by 2, else multiply k by 2.
Edge Case Check: If k is greater than or equal to the length of the string, return -1.
Mark Characters as Removed: Initialize a marked array to track removed characters. For each character, find the smallest character in the next k characters, remove it, and continue.
Build Result: After processing, collect all characters that remain and return the resulting string.
#include <bits/stdc++.h>
using namespace std;
int countSetBits(int n)
{
int count = 0;
while (n)
{
count += n & 1;
n >>= 1;
}
return count;
}
// Function to find the lexicographically smallest string
string lexicographicallySmallest(string &s, int k)
{
int n = s.size();
// Adjust k based on whether the length of the string is a power of 2
if (countSetBits(n) == 1)
{
k /= 2;
}
else
{
k *= 2;
}
if (k >= n)
{
return "-1";
}
vector<int> a(n, 1);
int i = 0, j;
// Iterate through the string
while (i < n)
{
int start = i;
int index = start;
int end = min(start + k, n - 1);
char minn = s[start];
// Find the smallest character in the range [start, end]
for (j = start + 1; j <= end; j++)
{
if (s[j] < minn)
{
minn = s[j];
index = j;
}
}
// Mark elements before the smallest as removed
for (j = index - 1; j >= start && k > 0; j--)
{
a[j] = 0;
k--;
}
// Move to the next position after the smallest
i = index + 1;
}
// If k is still not zero, remove from the end
if (k)
{
for (i = n - 1; i >= 0 && k > 0; i--)
{
if (a[i])
{
a[i] = 0;
k--;
}
}
}
// Build the result string
string res = "";
for (i = 0; i < n; i++)
{
if (a[i])
{
res += s[i];
}
}
return res;
}
int main()
{
string s = "fooland";
int k = 2;
cout << lexicographicallySmallest(s, k) << endl;
return 0;
}
import java.util.*;
public class GfG {
static int countSetBits(int n)
{
int count = 0;
while (n != 0) {
count += n & 1;
n >>= 1;
}
return count;
}
static String lexicographicallySmallest(String s, int k)
{
int n = s.length();
// Adjust k based on whether the length of the
// string is a power of 2
if (countSetBits(n) == 1) {
k /= 2;
}
else {
k *= 2;
}
if (k >= n) {
return "-1";
}
int[] a = new int[n];
Arrays.fill(a, 1);
int i = 0;
// Iterate through the string
while (i < n) {
int start = i;
int index = start;
int end = Math.min(start + k, n - 1);
char minn = s.charAt(start);
// Find the smallest character in the range
// [start, end]
for (int j = start + 1; j <= end; j++) {
if (s.charAt(j) < minn) {
minn = s.charAt(j);
index = j;
}
}
// Mark elements before the smallest as removed
for (int j = index - 1; j >= start && k > 0;
j--) {
a[j] = 0;
k--;
}
// Update i to move to the next position after
// the smallest
i = index + 1;
}
// If k is still not zero, remove from the end
if (k > 0) {
for (i = n - 1; i >= 0 && k > 0; i--) {
if (a[i] == 1) {
a[i] = 0;
k--;
}
}
}
// Build the result string from the marked positions
StringBuilder res = new StringBuilder();
for (i = 0; i < n; i++) {
if (a[i] == 1) {
res.append(s.charAt(i));
}
}
return res.toString();
}
public static void main(String[] args)
{
String s = "fooland";
int k = 2;
System.out.println(lexicographicallySmallest(s, k));
}
}
def count_set_bits(n):
count = 0
while n:
count += n & 1
n >>= 1
return count
# Function to find the lexicographically smallest string
def lexicographicallySmallest(s, k):
n = len(s)
# Adjust k based on whether the length of the string is a power of 2
if count_set_bits(n) == 1:
k //= 2
else:
k *= 2
if k >= n:
return "-1"
a = [1] * n
i = 0
# Iterate through the string
while i < n:
start = i
index = start
end = min(start + k, n - 1)
minn = s[start]
# Find the smallest character in the range [start, end]
for j in range(start + 1, end + 1):
if s[j] < minn:
minn = s[j]
index = j
# Mark elements before the smallest as removed
for j in range(index - 1, start - 1, -1):
if k > 0:
a[j] = 0
k -= 1
# Update i to move to the next position after the smallest
i = index + 1
# If k is still not zero, remove from the end
if k:
for i in range(n - 1, -1, -1):
if a[i] and k > 0:
a[i] = 0
k -= 1
# Build the result string from the marked positions
res = ""
for i in range(n):
if a[i]:
res += s[i]
return res
# Driver Code
if __name__ == "__main__":
s = "fooland"
k = 2
print(lexicographicallySmallest(s, k))
using System;
using System.Collections.Generic;
class GfG
{
static int CountSetBits(int n)
{
int count = 0;
while (n != 0)
{
count += n & 1;
n >>= 1;
}
return count;
}
static string lexicographicallySmallest(string s, int k)
{
int n = s.Length;
// Adjust k based on whether the length of the string is a power of 2
if (CountSetBits(n) == 1)
{
k /= 2;
}
else
{
k *= 2;
}
// Edge case: If k is greater than or equal to the string length, return "-1"
if (k >= n)
{
return "-1";
}
int[] a = new int[n];
for (int i = 0; i < n; i++)
{
a[i] = 1; // Mark all positions as taken (1)
}
int idx = 0; // Renamed variable to avoid conflict
int j;
// Iterate through the string
while (idx < n)
{
int start = idx;
int index = start;
int end = Math.Min(start + k, n - 1);
char minn = s[start];
// Find the smallest character in the range [start, end]
for (j = start + 1; j <= end; j++)
{
if (s[j] < minn)
{
minn = s[j];
index = j;
}
}
// Mark elements before the smallest as removed
for (j = index - 1; j >= start && k > 0; j--)
{
a[j] = 0;
k--;
}
// Update idx to the next position after the smallest
idx = index + 1;
}
// If k is still not zero, remove from the end
if (k > 0)
{
for (int i = n - 1; i >= 0 && k > 0; i--)
{
if (a[i] == 1)
{
a[i] = 0;
k--;
}
}
}
// Build the result string from the marked positions
string res = "";
for (int i = 0; i < n; i++)
{
if (a[i] == 1)
{
res += s[i];
}
}
return res;
}
static void Main()
{
string s = "fooland";
int k = 2;
Console.WriteLine(lexicographicallySmallest(s, k));
}
}
function countSetBits(n) {
let count = 0;
while (n) {
count += n & 1;
n >>= 1;
}
return count;
}
// Function to find the lexicographically smallest string
function lexicographicallySmallest(s, k) {
const n = s.length;
// Adjust k based on whether the length of the string is a power of 2
if (countSetBits(n) === 1) {
k = Math.floor(k / 2);
} else {
k *= 2;
}
if (k >= n) {
return "-1";
}
const a = new Array(n).fill(1);
let i = 0;
// Iterate through the string
while (i < n) {
const start = i;
let index = start;
const end = Math.min(start + k, n - 1);
let minn = s[start];
// Find the smallest character in the range [start, end]
for (let j = start + 1; j <= end; j++) {
if (s[j] < minn) {
minn = s[j];
index = j;
}
}
// Mark elements before the smallest as removed
for (let j = index - 1; j >= start && k > 0; j--) {
a[j] = 0;
k--;
}
// Update i to move to the next position after the smallest
i = index + 1;
}
// If k is still not zero, remove from the end
if (k) {
for (i = n - 1; i >= 0 && k > 0; i--) {
if (a[i]) {
a[i] = 0;
k--;
}
}
}
// Build the result string from the marked positions
let res = "";
for (i = 0; i < n; i++) {
if (a[i]) {
res += s[i];
}
}
return res;
}
// Driver code
const s = "fooland";
const k = 2;
console.log(lexicographicallySmallest(s, k));
Output
and
[Expected Approach] Using Stack-Based Removal - O(n + k) Time and O(n) Space
We mainly need to find the lexicographically smallest subsequence of length n-k. The idea is to use stack and maintain at least (n â k) non-decreasing characters starting with the smallest character we found.
- Adjust
k: If the string length is a power of 2, dividekby 2; otherwise, multiplykby 2. - Edge Case: If
kis greater than or equal to the string length, return-1. - Use a Stack: Iterate over the string. For each character, decrease k and remove the top stack elements if they are greater and
k > 0. Push the current character onto the stack. - Remove Remaining Characters: If not enough characters are removed, pop characters from the stack until
kreaches 0. - Build Result: Collect the remaining characters from the stack, reverse them, and return the result.
#include <bits/stdc++.h>
using namespace std;
string lexicographicallySmallest(string &s, int k)
{
string ans = "";
int l = s.length();
// Adjust k based on whether the length of the string is a power of 2
if (l & (l - 1))
k += k;
else
k /= 2;
// If k is greater than or equal to the string length, return -1
if (k >= l)
return "-1";
stack<char> st;
// Process each character in the string
for (int i = 0; i < l; i++)
{
while (!st.empty() && k > 0 && st.top() > s[i])
{
st.pop();
k--;
}
st.push(s[i]);
}
// If k is still greater than 0, remove remaining characters from the stack
if (k > 0)
while (k--)
st.pop();
// Build the result string from the stack
while (!st.empty())
{
ans += st.top();
st.pop();
}
// Reverse the result to get the final lexicographically smallest string
reverse(ans.begin(), ans.end());
return ans;
}
int main()
{
string s = "fooland";
int k = 2;
cout << lexicographicallySmallest(s, k) << endl;
return 0;
}
import java.util.Stack;
public class GfG {
public static String lexicographicallySmallest(String s,
int k)
{
StringBuilder ans = new StringBuilder();
int l = s.length();
// Adjust k based on whether the length of the
// string is a power of 2
if ((l & (l - 1)) != 0)
k += k;
else
k /= 2;
// If k is greater than or equal to the string
// length, return -1
if (k >= l)
return "-1";
Stack<Character> st = new Stack<>();
// Process each character in the string
for (int i = 0; i < l; i++) {
while (!st.isEmpty() && k > 0
&& st.peek() > s.charAt(i)) {
st.pop();
k--;
}
st.push(s.charAt(i));
}
// If k is still greater than 0, remove remaining
// characters from the stack
if (k > 0)
while (k-- > 0)
st.pop();
// Build the result string from the stack
while (!st.isEmpty()) {
ans.append(st.pop());
}
// Reverse the result to get the final
// lexicographically smallest string
return ans.reverse().toString();
}
public static void main(String[] args)
{
String s = "fooland";
int k = 2;
System.out.println(lexicographicallySmallest(s, k));
}
}
def lexicographicallySmallest(s, k):
ans = ""
l = len(s)
# Adjust k based on whether the length of the string is a power of 2
if l & (l - 1):
k += k
else:
k //= 2
# If k is greater than or equal to the string length, return -1
if k >= l:
return "-1"
st = []
# Process each character in the string
for i in range(l):
while st and k > 0 and st[-1] > s[i]:
st.pop()
k -= 1
st.append(s[i])
# If k is still greater than 0, remove remaining characters from the stack
if k > 0:
while k > 0:
st.pop()
k -= 1
# Build the result string from the stack
while st:
ans += st.pop()
# Reverse the result to get the final lexicographically smallest string
return ans[::-1]
# Driver Code
if __name__ == '__main__':
s = "fooland"
k = 2
print(lexicographicallySmallest(s, k))
using System;
using System.Collections.Generic;
class GfG {
static string lexicographicallySmallest(string s, int k)
{
string ans = "";
int l = s.Length;
// Adjust k based on whether the length of the
// string is a power of 2
if ((l & (l - 1)) != 0)
k += k;
else
k /= 2;
// If k is greater than or equal to the string
// length, return -1
if (k >= l)
return "-1";
Stack<char> st = new Stack<char>();
// Process each character in the string
for (int i = 0; i < l; i++) {
while (st.Count > 0 && k > 0
&& st.Peek() > s[i]) {
st.Pop();
k--;
}
st.Push(s[i]);
}
// If k is still greater than 0, remove remaining
// characters from the stack
if (k > 0)
while (k-- > 0)
st.Pop();
// Build the result string from the stack
while (st.Count > 0) {
ans += st.Pop();
}
// Reverse the result to get the final
// lexicographically smallest string
char[] charArray = ans.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
static void Main()
{
string s = "fooland";
int k = 2;
Console.WriteLine(lexicographicallySmallest(s, k));
}
}
function lexicographicallySmallest(s, k)
{
let ans = "";
const l = s.length;
// Adjust k based on whether the length of the string is
// a power of 2
if (l & (l - 1))
k += k;
else
k = Math.floor(k / 2);
// If k is greater than or equal to the string length,
// return -1
if (k >= l)
return "-1";
const st = [];
// Process each character in the string
for (let i = 0; i < l; i++) {
while (st.length > 0 && k > 0
&& st[st.length - 1] > s[i]) {
st.pop();
k--;
}
st.push(s[i]);
}
// If k is still greater than 0, remove remaining
// characters from the stack
if (k > 0)
while (k-- > 0)
st.pop();
// Build the result string from the stack
while (st.length > 0) {
ans += st.pop();
}
// Reverse the result to get the final lexicographically
// smallest string
return ans.split("").reverse().join("");
}
// Driver Code
const s = "fooland";
const k = 2;
console.log(lexicographicallySmallest(s, k));
Output
and
Time Complexity: O(n + k), for traversal of every element of the string and inside the loop we traverse at most k times for the removal of strings from the stack.
Auxiliary Space: O(n), For storing characters in the stack.