Open In App

Lexicographically smallest String by removing exactly K characters

Last Updated : 04 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

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".

[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.

C++
#include <iostream>
#include <vector>
#include <algorithm>

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

        // 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
    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 << lexSmallest(s, k) << endl;

    return 0;
}
Java
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 lexSmallest(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(lexSmallest(s, k));
    }
}
Python
def count_set_bits(n):
    count = 0
    while n:
        count += n & 1
        n >>= 1
    return count

# Function to find the lexicographically smallest string
def lex_smallest(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

s = "fooland"
k = 2

print(lex_smallest(s, k))
C#
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 LexSmallest(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(LexSmallest(s, k)); 
    }
}
JavaScript
function countSetBits(n) {
    let count = 0;
    while (n) {
        count += n & 1;
        n >>= 1;
    }
    return count;
}

// Function to find the lexicographically smallest string
function lexSmallest(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 /= 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;
}

const s = "fooland";
const k = 2;

console.log(lexSmallest(s, k));

Output
and

[Expected Approach] Stack-Based Removal - O(n + k) Time and O(n) Space

We mainly need to find the lexicagraphically 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.

Steps:

  1. Adjust k: If the string length is a power of 2, divide k by 2; otherwise, multiply k by 2.
  2. Edge Case: If k is greater than or equal to the string length, return -1.
  3. 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.
  4. Remove Remaining Characters: If not enough characters are removed, pop characters from the stack until k reaches 0.
  5. Build Result: Collect the remaining characters from the stack, reverse them, and return the result.

Illustration:

Let us understand with "XAYB" and k = 2
i = 0, stack = [X]
i = 1, A is smaller than stack top, remove the top and make k = 1, stack = [A]
i = 2, Y is greater, push, stack = [A, Y]
i = 3, B is smaller, pop and make k = 0, stack = [A, B]

Return "AB"

C++
#include <iostream>
#include <stack>
#include <algorithm>
using namespace std;

string lexSmallest(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 << lexSmallest(s, k) << endl;

    return 0;
}
Java
import java.util.Stack;

public class GfG {
    public static String lexSmallest(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(lexSmallest(s, k));
    }
}
Python
def lexSmallest(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]

if __name__ == '__main__':
    s = "fooland"
    k = 2

    print(lexSmallest(s, k))
C#
using System;
using System.Collections.Generic;

class GfG {
    static string LexSmallest(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(LexSmallest(s, k));
    }
}
JavaScript
function lexSmallest(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('');
}

const s = "fooland";
const k = 2;

console.log(lexSmallest(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.


Similar Reads