# 3170. Lexicographically Minimum String After Removing Stars

## Description

You are given a string s. It may contain any number of '*' characters. Your task is to remove all '*' characters.

While there is a '*', do the following operation:

• Delete the leftmost '*' and the smallest non-'*' character to its left. If there are several smallest characters, you can delete any of them.

Return the lexicographically smallest resulting string after removing all '*' characters.

Example 1:

Input: s = "aaba*"

Output: "aab"

Explanation:

We should delete one of the 'a' characters with '*'. If we choose s[3], s becomes the lexicographically smallest.

Example 2:

Input: s = "abc"

Output: "abc"

Explanation:

There is no '*' in the string.

Constraints:

• 1 <= s.length <= 105
• s consists only of lowercase English letters and '*'.
• The input is generated such that it is possible to delete all '*' characters.

## Solutions

### Solution 1: Record Indices by Character

We define an array $g$ to record the index list of each character, and a boolean array $rem$ of length $n$ to record whether each character needs to be deleted.

We traverse the string $s$:

If the current character is an asterisk, we need to delete it, so we mark $rem[i]$ as deleted. At the same time, we need to delete the character with the smallest lexicographical order and the largest index at this time. We traverse the 26 lowercase letters in ascending order. If $g[a]$ is not empty, we delete the last index in $g[a]$ and set the corresponding index in $rem$ as deleted.

If the current character is not an asterisk, we add the index of the current character to $g$.

Finally, we traverse $s$ and concatenate the undeleted characters.

The time complexity is $O(n \times |\Sigma|)$, and the space complexity is $O(n)$. Where $n$ is the length of the string $s$, and $|\Sigma|$ is the size of the character set. In this problem, $|\Sigma| = 26$.

• class Solution {
public String clearStars(String s) {
Deque<Integer>[] g = new Deque[26];
Arrays.setAll(g, k -> new ArrayDeque<>());
int n = s.length();
boolean[] rem = new boolean[n];
for (int i = 0; i < n; ++i) {
if (s.charAt(i) == '*') {
rem[i] = true;
for (int j = 0; j < 26; ++j) {
if (!g[j].isEmpty()) {
rem[g[j].pop()] = true;
break;
}
}
} else {
g[s.charAt(i) - 'a'].push(i);
}
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < n; ++i) {
if (!rem[i]) {
sb.append(s.charAt(i));
}
}
return sb.toString();
}
}

• class Solution {
public:
string clearStars(string s) {
stack<int> g[26];
int n = s.length();
vector<bool> rem(n);
for (int i = 0; i < n; ++i) {
if (s[i] == '*') {
rem[i] = true;
for (int j = 0; j < 26; ++j) {
if (!g[j].empty()) {
rem[g[j].top()] = true;
g[j].pop();
break;
}
}
} else {
g[s[i] - 'a'].push(i);
}
}
string ans;
for (int i = 0; i < n; ++i) {
if (!rem[i]) {
ans.push_back(s[i]);
}
}
return ans;
}
};

• class Solution:
def clearStars(self, s: str) -> str:
g = defaultdict(list)
n = len(s)
rem = [False] * n
for i, c in enumerate(s):
if c == "*":
rem[i] = True
for a in ascii_lowercase:
if g[a]:
rem[g[a].pop()] = True
break
else:
g[c].append(i)
return "".join(c for i, c in enumerate(s) if not rem[i])


• func clearStars(s string) string {
g := make([][]int, 26)
n := len(s)
rem := make([]bool, n)
for i, c := range s {
if c == '*' {
rem[i] = true
for j := 0; j < 26; j++ {
if len(g[j]) > 0 {
rem[g[j][len(g[j])-1]] = true
g[j] = g[j][:len(g[j])-1]
break
}
}
} else {
g[c-'a'] = append(g[c-'a'], i)
}
}
ans := []byte{}
for i := range s {
if !rem[i] {
ans = append(ans, s[i])
}
}
return string(ans)
}

• function clearStars(s: string): string {
const g: number[][] = Array.from({ length: 26 }, () => []);
const n = s.length;
const rem: boolean[] = Array(n).fill(false);
for (let i = 0; i < n; ++i) {
if (s[i] === '*') {
rem[i] = true;
for (let j = 0; j < 26; ++j) {
if (g[j].length) {
rem[g[j].pop()!] = true;
break;
}
}
} else {
g[s.charCodeAt(i) - 97].push(i);
}
}
return s
.split('')
.filter((_, i) => !rem[i])
.join('');
}