Lexicographically largest string after k removals
Last Updated :
20 Jun, 2025
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));
[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));
Explore
DSA Fundamentals
Data Structures
Algorithms
Advanced
Interview Preparation
Practice Problem