Open In App

Lexicographically largest string after k removals

Last Updated : 20 Jun, 2025
Comments
Improve
Suggest changes
6 Likes
Like
Report

Given a string s and an integer k, find the lexicographically largest string that can be obtained by removing exactly k characters from s, while preserving the relative order of the remaining characters.

Examples:

Input: s = "zebra", k = 3
Output: "zr"
Explanation: Removing "e", "b", and "a" results in "zr", which is lexicographically the greatest.

Input: s = "ritz", k = 2
Output: "tz"
Explanation: By removing two characters in all possible ways, we get: "ri", "rt", "rz", "it", "iz", and "tz". Among these, "tz" is lexicographically the largest.

Input: s = "jackie", k = 2
Output: "jkie"
Explanation: Removing "a" and "c" gives "jkie", which is the largest string possible in dictionary order.

[Naive Approach] Generate All Subsequences - O(2^n) Time and O(n) Space

The idea is to generate all subsequences of length n - k from the original string by recursively choosing to either include or skip each character. The thought process is that since we need the lexicographically largest string, we must explore every valid combination and track the maximum encountered so far.

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

// Function to generate all subsequences of length targetLen
void dfsSearch(string& s, int idx, string curr, 
                          int targetLen, string& ans) {

    // If desired length is reached
    if (curr.length() == targetLen) {
        ans = max(ans, curr); 
        return;
    }

    // If end of string is reached
    if (idx == s.length()) {
        return;
    }

    // Include current character
    dfsSearch(s, idx + 1, curr + s[idx], 
                         targetLen, ans);

    // Exclude current character
    dfsSearch(s, idx + 1, curr, targetLen, ans);
}

// Function to get lexicographically largest
// subsequence of length n - k
string maxSubseq(string &s, int k) {

    int n = s.length();
    int targetLen = n - k;
    string ans = "";

    // Generate all valid subsequences and update ans
    dfsSearch(s, 0, "", targetLen, ans);

    return ans;
}

int main() {

    string s = "zebra";
    int k = 3;

    cout << maxSubseq(s, k) << endl;

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

class GfG {

    // Function to generate all subsequences of length targetLen
    static void dfsSearch(String s, int idx, String curr, 
                                     int targetLen, String[] ans) {

        // If desired length is reached
        if (curr.length() == targetLen) {
            if (curr.compareTo(ans[0]) > 0) {
                ans[0] = curr;
            }
            return;
        }

        // If end of string is reached
        if (idx == s.length()) {
            return;
        }

        // Include current character
        dfsSearch(s, idx + 1, curr + s.charAt(idx), 
                             targetLen, ans);

        // Exclude current character
        dfsSearch(s, idx + 1, curr, targetLen, ans);
    }

    // Function to get lexicographically largest
    // subsequence of length n - k
    static String maxSubseq(String s, int k) {

        int n = s.length();
        int targetLen = n - k;
        String[] ans = {""};

        // Generate all valid subsequences and update ans
        dfsSearch(s, 0, "", targetLen, ans);

        return ans[0];
    }

    public static void main(String[] args) {

        String s = "zebra";
        int k = 3;

        System.out.println(maxSubseq(s, k));
    }
}
Python
# Function to generate all subsequences of length targetLen
def dfsSearch(s, idx, curr, targetLen, ans):

    # If desired length is reached
    if len(curr) == targetLen:
        ans[0] = max(ans[0], curr)
        return

    # If end of string is reached
    if idx == len(s):
        return

    # Include current character
    dfsSearch(s, idx + 1, curr + s[idx], targetLen, ans)

    # Exclude current character
    dfsSearch(s, idx + 1, curr, targetLen, ans)

# Function to get lexicographically largest
# subsequence of length n - k
def maxSubseq(s, k):

    n = len(s)
    targetLen = n - k
    ans = [""]

    # Generate all valid subsequences and update ans
    dfsSearch(s, 0, "", targetLen, ans)

    return ans[0]

if __name__ == "__main__":

    s = "zebra"
    k = 3

    print(maxSubseq(s, k))
C#
using System;

class GfG {

    // Function to generate all subsequences of length targetLen
    static void dfsSearch(string s, int idx, string curr, 
                                     int targetLen, ref string ans) {

        // If desired length is reached
        if (curr.Length == targetLen) {
            if (String.Compare(curr, ans) > 0) {
                ans = curr;
            }
            return;
        }

        // If end of string is reached
        if (idx == s.Length) {
            return;
        }

        // Include current character
        dfsSearch(s, idx + 1, curr + s[idx], 
                             targetLen, ref ans);

        // Exclude current character
        dfsSearch(s, idx + 1, curr, targetLen, ref ans);
    }

    // Function to get lexicographically largest
    // subsequence of length n - k
    static string maxSubseq(string s, int k) {

        int n = s.Length;
        int targetLen = n - k;
        string ans = "";

        // Generate all valid subsequences and update ans
        dfsSearch(s, 0, "", targetLen, ref ans);

        return ans;
    }

    static void Main() {

        string s = "zebra";
        int k = 3;

        Console.WriteLine(maxSubseq(s, k));
    }
}
JavaScript
// Function to generate all subsequences of length targetLen
function dfsSearch(s, idx, curr, targetLen, ans) {

    // If desired length is reached
    if (curr.length === targetLen) {
        ans[0] = curr > ans[0] ? curr : ans[0];
        return;
    }

    // If end of string is reached
    if (idx === s.length) {
        return;
    }

    // Include current character
    dfsSearch(s, idx + 1, curr + s[idx], targetLen, ans);

    // Exclude current character
    dfsSearch(s, idx + 1, curr, targetLen, ans);
}

// Function to get lexicographically largest
// subsequence of length n - k
function maxSubseq(s, k) {

    let n = s.length;
    let targetLen = n - k;
    let ans = [""];

    // Generate all valid subsequences and update ans
    dfsSearch(s, 0, "", targetLen, ans);

    return ans[0];
}

// Driver Code
let s = "zebra";
let k = 3;

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

Output
zr

[Expected Approach] Greedy Stack-Based Subsequence Selection - O(n) Time and O(n) Space

The idea is to use a greedy approach with a stack to find the largest possible subsequence of length n - k. The thought process is to remove characters from the stack if they are smaller than the current character and if we still have deletions left (k > 0). This ensures that we always keep characters that help form a lexicographically larger result while preserving the original order.

Instead of creating an explicit stack, we use the result string itself as a stack, we push and pop characters at the end of the result string.

Illustration

C++
#include <iostream> 
#include <string> 
using namespace std;

string maxSubseq(string &s, int k) {
    int n = s.length();
    
    // Keep original k untouched
    int toRemove = k; 
    string res = "";

    for (int i = 0; i < n; i++) {
    
        // Remove smaller characters from the end if we still have quota
        while (!res.empty() && toRemove > 0 && res.back() < s[i]) {
            res.pop_back();
            toRemove--;
        }
        res.push_back(s[i]);
    }

    // The required length is n - k, so slice accordingly
    res.resize(n - k);
    return res;
}

int main() {
    
    string s = "zebra";
    int k = 3;
    cout << maxSubseq(s, k) << endl; 
    return 0;
}
Java
class GfG{
    
    // Function to get lexicographically largest subsequence of length n - k
    public static String maxSubseq(String s, int k) {
        int n = s.length();
        
        // Keep a copy of k to track deletions
        int toRemove = k;
        StringBuilder res = new StringBuilder();

        for (int i = 0; i < n; i++) {
            while (res.length() > 0 && toRemove > 0 &&
                   res.charAt(res.length() - 1) < s.charAt(i)) {
                res.deleteCharAt(res.length() - 1);
                toRemove--;
            }
            res.append(s.charAt(i));
        }

        // Result should be of length n - k
        return res.substring(0, n - k);
    }

    public static void main(String[] args) {
        
        String s = "zebra";
        int k = 3;
        System.out.println(maxSubseq(s, k)); 
    }
}
Python
def maxSubseq(s, k):
    n = len(s)
    res = ""
    # Keep a separate copy of k
    to_remove = k  

    # Build the result greedily
    for i in range(n):
        while res and to_remove > 0 and res[-1] < s[i]:
            res = res[:-1]
            to_remove -= 1
        res += s[i]
    
    # Result should be of length n - k
    return res[:n - k]

if __name__ == "__main__":
    s = "zebra"
    k = 3
    print(maxSubseq(s, k))  
C#
using System;
using System.Text;

class GfG {
    
    // Function to get lexicographically largest
    // subsequence of length n - k
    static string maxSubseq(string s, int k) {
        int n = s.Length;
        StringBuilder res = new StringBuilder();
    
        // Keep a separate variable since k will change
        int toRemove = k; 

        for (int i = 0; i < n; i++) {
            while (res.Length > 0 && toRemove > 0 && 
                   res[res.Length - 1] < s[i]) {
                // Remove last character
                res.Length--; 
                toRemove--;
            }

            res.Append(s[i]);
        }

        // Trim to desired length (n - k)
        return res.ToString().Substring(0, n - k);
    }

    static void Main() {
        
        string s = "zebra";
        int k = 3;
        Console.WriteLine(maxSubseq(s, k));
    }
}
JavaScript
function maxSubseq(s, k) {
    let n = s.length;
    let res = "";
    // Track remaining removals separately
    let toRemove = k; 

    for (let i = 0; i < n; i++) {
        // While the last char in res is smaller and we can remove more
        while (res.length > 0 && toRemove > 0 && res[res.length - 1] < s[i]) {
            res = res.slice(0, -1);
            toRemove--;
        }

        res += s[i];
    }

    // Final length should be n - k characters
    return res.slice(0, n - k);
}

// Driver Code
let s = "zebra";
let k = 3;
console.log(maxSubseq(s, k)); 

Output
zr

Article Tags :

Explore