2390. Removing Stars From a String

MediumStackStringSimulation
Leetcode Link

Problem Description

In this problem, you are given a string s which consists of regular characters mixed with star characters (*). Your task is to modify the string by repeatedly performing a specific operation until all star characters are removed.

The operation allowed is:

  • Select a star character in the string s.
  • Remove the character that is immediately to the left of the selected star (this will always be a non-star character due to the provided input guarantees).
  • Remove the star itself.

You must continue performing this operation until there are no more stars in the string. The problem guarantees that it is always possible to perform the operation, meaning you will never run out of non-star characters on the left side of a star to remove. The problem also assures you that the final string will be unique, so there is only one correct solution.

The goal is to return the resulting string after all the stars and their adjacent left characters have been removed.

Intuition

The intuition behind the solution is to process the given string s from left to right, keeping track of the characters that would remain after each star operation. To implement this approach efficiently, we can use a stack data structure, which provides an easy way to remove the last character added when we encounter a star.

Here's how the solution approach is derived:

  • Initialize an empty list ans which will function as a stack to hold the characters that would remain in the string.
  • Iterate over each character c in the string s.
    • If the current character c is a star (*), it indicates that we need to remove the character nearest to its left. In stack terms, this means we need to pop the last element from the stack.
    • If the current character c is not a star, we should add this character to the stack, as it is (for now) a part of the resulting string.
  • After we finish iterating through the string s, the stack contains all the characters that survived the star operations.
  • We then join the characters in the stack to form the final string, which is the string after all stars and their adjacent left characters have been removed.
  • Return the final string as a result.

This approach simulates the given operations by keeping a dynamic list of surviving characters, which allows us to efficiently apply the given rules and arrive at the correct result.

Learn more about Stack patterns.

Not Sure What to Study? Take the 2-min Quiz to Find Your Missing Piece:

Consider the classic dynamic programming of longest increasing subsequence:

Find the length of the longest subsequence of a given sequence such that all elements of the subsequence are sorted in increasing order.

For example, the length of LIS for [50, 3, 10, 7, 40, 80] is 4 and LIS is [3, 7, 40, 80].

What is the recurrence relation?

Solution Approach

The solution to the problem utilizes a stack-based approach to process the input string. A stack is a data structure that allows adding (pushing) elements to the end and removing (popping) the last element added.

Here's how we implement the solution step-by-step:

  1. Initialize an empty list ans, which we will use as a stack to keep track of the surviving characters in the string s.

    1ans = []
  2. Iterate over each character c in the string s.

    1for c in s:
  3. For each character c encountered, check whether it is a star (*) or not.

    1if c == '*':
    • If c is a star, we need to remove the most recent character added to the ans list, which represents the character to the left of the star in the original string. Since ans acts as our stack, we can simply pop the last element from it:

      1ans.pop()
    • If c is not a star, this character is potentially part of the final string and should be added (pushed) to the ans list.

      1else:
      2    ans.append(c)

    The key algorithmic insight here is that the stack automatically takes care of the 'closest non-star character to its left' detail mentioned in the problem description. The last element on the stack will always be the closest character to any star that we encounter.

  4. After the loop has finished, all stars have been processed, and ans contains only the characters that are part of the final string. We need to reconstruct the string from the list.

    1return ''.join(ans)

    Using the join method on an empty string and passing ans as an argument concatenates all the elements in ans into a single string without any delimiters between them (since we're joining with an empty string).

  5. Return the final string as the result of the removeStars method.

By following these steps and applying a simple stack-based algorithm, we are able to simulate the character removal process specified in the problem statement and arrive at the correct solution in a clear and efficient manner.

Discover Your Strengths and Weaknesses: Take Our 2-Minute Quiz to Tailor Your Study Plan:

What is the worst case running time for finding an element in a binary tree (not necessarily binary search tree) of size n?

Example Walkthrough

Let's use a simple example to illustrate the solution approach. Consider the string s = "ab*c*"; our task is to remove the star characters (*) along with the immediate left non-star character until no stars are left in the string.

  1. Initialize the stack ans as an empty list.

    1ans = []
  2. Iterate over each character in s, which is "ab*c*". We will process the characters one by one:

    • When we see 'a', since it is not a star, we add it to ans.

      1ans = ['a']
    • When we see 'b', again it is not a star, so we add it to ans.

      1ans = ['a', 'b']
    • Now we encounter '*'. It's a star, so we need to remove the most recent non-star character, which is 'b'. We pop 'b' from ans.

      1ans = ['a']
    • Next, we see 'c', which is not a star. So we add 'c' to ans.

      1ans = ['a', 'c']
    • Finally, we encounter another star '*'. We remove the latest non-star character 'c' by popping it from ans.

      1ans = ['a']
  3. With all the characters processed, we now have the final ans stack:

    1ans = ['a']

    This represents the characters that survived after all the star operations.

  4. Convert the ans stack back into a string:

    1return ''.join(ans)

    This results in the string "a".

  5. Return the final string "a", which is the string after all stars and their adjacent left characters have been removed.

Using this example, we can see how the stack-based approach efficiently simulates the operation of removing a star along with its immediately left non-star character. Since we keep adding non-star characters to the stack and only pop when we see a star, the stack maintains a correct representation of the characters to the left that have not been removed by any star operation.

Solution Implementation

1class Solution:
2    def removeStars(self, s: str) -> str:
3        # Initialize an empty list to act as a stack
4        result_stack = []
5      
6        # Iterate over each character in the string
7        for char in s:
8            # If the character is a star, we pop the last element from the stack
9            # This simulates removing the character before a star
10            if char == '*':
11                result_stack.pop()
12            # If the character is not a star, we add the character to the stack
13            else:
14                result_stack.append(char)
15      
16        # Join the characters in the stack to get the final string
17        # This represents the string after all the removals
18        return ''.join(result_stack)
19
1class Solution {
2  
3    // Method to remove characters from a string that are followed by a star.
4    // Each star (*) character means a backspace operation on the current string.
5    public String removeStars(String s) {
6        // Initialize a StringBuilder to hold the resulting characters after backspaces are applied.
7        StringBuilder resultBuilder = new StringBuilder();
8      
9        // Iterate over each character in the string.
10        for (int i = 0; i < s.length(); ++i) {
11            // Check if the current character is a star
12            if (s.charAt(i) == '*') {
13                // If star, remove the preceding character from the StringBuilder if it exists
14                if (resultBuilder.length() > 0) {
15                    resultBuilder.deleteCharAt(resultBuilder.length() - 1);
16                }
17            } else {
18                // If not a star, append the character to the StringBuilder
19                resultBuilder.append(s.charAt(i));
20            }
21        }
22        // Convert the StringBuilder back to a String and return it.
23        return resultBuilder.toString();
24    }
25}
26
1class Solution {
2public:
3    // Function to remove characters followed by a star (*) character in the string
4    string removeStars(string s) {
5        // The result string that will store the final output after removal
6        string result;
7
8        // Iterate through each character in the input string
9        for (char c : s) {
10            if (c == '*') {
11                // If current character is a star, remove the last character from result
12                if (!result.empty()) { // Ensure there is a character to remove
13                    result.pop_back();
14                }
15            } else {
16                // Otherwise, append the current character to the result string
17                result.push_back(c);
18            }
19        }
20
21        // Return the modified string after all stars and their preceding characters are removed
22        return result;
23    }
24};
25
1// This function removes all characters that are followed by a '*' in a given string.
2// @param {string} s - The input string containing characters and '*'
3// @return {string} - The modified string with characters followed by '*' removed
4function removeStars(s: string): string {
5    // Initialize an array to act as a stack to manage the characters
6    const result: string[] = [];
7
8    // Loop through each character in the input string
9    for (const char of s) {
10        if (char === '*') {
11            // If the current character is '*', remove the last character from the stack
12            result.pop();
13        } else {
14            // Otherwise, add the current character to the stack
15            result.push(char);
16        }
17    }
18
19    // Join the characters in the stack to form the final string and return it
20    return result.join('');
21}
22
Not Sure What to Study? Take the 2-min Quiz:

Which data structure is used in a depth first search?

Time and Space Complexity

The given Python code implements a function to process a string by removing characters that should be deleted by a subsequent asterisk (*). The algorithm uses a stack represented by a list (ans) to manage the characters of the string.

Time Complexity:

The time complexity of the code is O(n), where n is the length of the input string s. This is because the code scans the string once, and for each character, it either appends it to the stack or pops the last character from the stack. Both appending to and popping from the end of a list in Python are operations that run in constant time, O(1).

Therefore, the loop iterates n times, with O(1) work for each iteration, resulting in a total time complexity of O(n).

Space Complexity:

The space complexity of the code is also O(n), which is the space needed to store the stack (ans) in the worst case where there are no asterisks in the string. In this case, all characters will be appended to the stack.

However, if there are asterisks, each asterisk reduces the size of the stack by one, potentially lowering the space used. The actual space usage depends on the distribution of non-asterisk characters and asterisks in the string, but the worst-case space complexity remains O(n), as we are always limited by the length of the initial string.

Learn more about how to find time and space complexity quickly using problem constraints.

Fast Track Your Learning with Our Quick Skills Quiz:

A heap is a ...?


Recommended Readings


Got a question? Ask the Teaching Assistant anything you don't understand.

Still not clear? Ask in the Forum,  Discord or Submit the part you don't understand to our editors.


TA 👨‍🏫