LeetCode #2390 Medium

Removing Stars From a String

Removing Stars From a String: each * deletes the closest non-star character to its left, along with the star itself. Return the string once every star has been applied.

Constraints
  • 1 <= s.length <= 10⁵
  • s consists of lowercase English letters and stars *.
  • The operation is always possible.
stringstack
Open on LeetCode ↗
02

Intuition

Removing stars from a string processes a string where each * deletes itself and the closest non-star character to its left. Stars are applied left to right, and the result is guaranteed non-empty of stars. Repeatedly scanning backwards for each star is O(n²). The observation that fixes it is about which character a star removes: - A star always deletes the most recently surviving character, which is precisely what a stack's top holds. So walk the string once: push ordinary characters, and on a *, pop instead. That single rule handles every case, including consecutive stars. In "ab**", the first star pops b and the second pops a, because after the first pop the top has changed. No rescanning is needed — the exposed top is always the correct next victim. The stack read bottom to top is the answer, already in order, with no reversal required. The problem guarantees enough characters exist for every star, so no empty-stack check is strictly needed — though adding one costs nothing and protects against malformed input. A character array or string builder works as well as a formal stack, since only the last element is ever inspected or removed. In Python a list with append and pop is exactly right. Building the final string with a single join is O(n); concatenating in the loop would be O(n²) in languages with immutable strings. Each character is pushed and popped at most once, giving O(n) time and O(n) space.

How to spot this pattern

'Closest to the left that still survives' is the stack signature, stated in plain English. Whenever a rule reaches backwards to the most recent unconsumed item, reach for a stack — the same reading solves Backspace String Compare, Remove All Adjacent Duplicates, and Asteroid Collision.

03

Approach

Try it first

Before reading on: as you scan left to right, which single character is a star always going to delete? Name the data structure whose top is exactly that character. Aim for one pass.

1

Identify what a star removes

A star deletes the most recently surviving character, which is exactly the top of a stack. That correspondence is the whole solution.

2

Push ordinary characters

Walk the string once, pushing every non-star character. The stack holds the survivors in their original order.

3

Pop on a star

A * removes itself and the top of the stack. One pop per star, with no scanning backwards.

4

Let consecutive stars cascade

In "ab**", the first star pops b and the second pops a — after each pop the exposed top is already the correct next victim.

5

Read the stack in order

Bottom to top is the answer, needing no reversal. Join once at the end rather than concatenating in the loop.

6

Cost of the approach

Each character is pushed and popped at most once, giving O(n) time and O(n) space for the stack.

04

Solution & live demo

▶1class Solution:
▶2 def removeStars(self, s):
▶3 stack = []
▶4 for ch in s:
▶5 if ch == "*":
▶6 stack.pop()
▶7 else:
▶8 stack.append(ch)
▶9 return "".join(stack)
05

Common pitfalls

Deleting from the string in a loop

✗ Wrong
while '*' in s:
    i = s.index('*')
    s = s[:i-1] + s[i+1:]
✓ Right
for ch in s:
    if ch == '*': stack.pop()
    else: stack.append(ch)

Every deletion rebuilds the string, so this is O(n²) and times out at 10⁵ characters. The stack achieves the same result in one pass.

Removing the star but not the character

✗ Wrong
if ch != '*':
    stack.append(ch)
✓ Right
if ch == '*':
    stack.pop()
else:
    stack.append(ch)

A star deletes two things: itself and the character to its left. Skipping stars without popping leaves every letter in place and returns the input with stars stripped.

Popping the wrong end

✗ Wrong
stack.pop(0)
✓ Right
stack.pop()

pop(0) removes the first character, not the nearest one to the left of the star. It is also O(n) per call, turning the solution quadratic as well as wrong.

06

Edge cases

No stars at all

Every character is pushed and nothing pops, so the input is returned unchanged.

Consecutive stars, e.g. "ab**"

Two pops peel off 'b' then 'a', leaving an empty string.

Star immediately after a letter, e.g. "a*"

The letter is pushed then popped, giving an empty result.

Everything removed

The stack empties and the answer is the empty string, which is valid.

Stars separated by letters, e.g. "leet*code"

Each star pops the current top, so deletions interleave correctly with pushes.

07

Complexity

Time
O(n)
Space
O(n)
Each character is pushed and popped at most once. The stack doubles as the output buffer.