Make The String Great

Updated on 16 June, 2025
Make The String Great header image

Problem Statement

In this problem, you are given a string s consisting of lowercase and uppercase English letters. The goal is to transform s into a "good" string. A "good" string is defined as one that does not contain two adjacent characters where one is the uppercase version of the other. More precisely, for any index i in the string, if s[i] is a lowercase letter then s[i + 1] should not be the uppercase version of s[i] and vice versa.

To make the string good, you can remove any pair of adjacent characters that make the string not good. This operation can be performed repeatedly until no more such adjacent pairs exist. The task is to return the string after all possible operations are performed. Importantly, an empty string is also considered to be a good string.

Examples

Example 1

Input:

s = "leEeVultrcode"

Output:

"Vultrcode"

Explanation:

In the first step, either you choose i = 1 or i = 2, both will result "leEeVultrcode" to be reduced to "Vultrcode".

Example 2

Input:

s = "abBAcC"

Output:

""

Explanation:

We have many possible scenarios, and all lead to the same answer. For example:
"abBAcC" --> "aAcC" --> "cC" --> ""
"abBAcC" --> "abBA" --> "aA" --> ""

Example 3

Input:

s = "s"

Output:

"s"

Constraints

  • 1 <= s.length <= 100
  • s contains only lower and upper case English letters.

Approach and Intuition

The core challenge here is to efficiently remove the offending adjacent pairs until no such pairs are left. Here's a strategic approach to tackle this problem:

  1. Initialize an empty list or stack to help in processing the string. This stack will store the characters of the string as it is being processed.

  2. Iterate through each character in the string:

    • For each character, check if the stack is not empty.

    • If the stack is non-empty, compare the current character with the top character of the stack:

      • If they are the same letter but in different cases (one uppercase and the other lowercase), pop the top character from the stack (i.e., remove both characters as they form a bad pair).
      • If they do not form such a pair, push the current character onto the stack.
  3. Continue this process for each character in the string. Each pair that forms a bad sequence is removed immediately, ensuring that the stack only contains characters that do not form bad pairs with their adjacent.

  4. Once the iteration through the string is complete, the stack will contain only the characters that are not part of any bad pair. Convert the stack back to a string.

This approach leverages the stack data structure to efficiently manage the characters, checking against the conditions specified in the problem. Its efficiency stems from dynamically adjusting the sequence of remaining characters, ensuring that you always have a sequence that can be evaluated in real-time for bad pairs. Moreover, using a stack allows for immediate removal of bad pairs, optimizing the process of making the string good. This technique is straightforward and guarantees that we will return the correct "good" string as the result.

Solutions

  • C++
  • Java
  • Python
cpp
class Solution {
public:
    string makeGood(string s) {
        vector<char> charStack;
        
        for (char ch : s) {
            if (!charStack.empty() && abs(charStack.back() - ch) == 32)
                charStack.pop_back();
            else
                charStack.push_back(ch);
        }
        
        string result(charStack.begin(), charStack.end());
        return result;
    }
};

The solution to the "Make The String Great" problem involves using a stack to iterate over a given string and intelligently remove adjacent characters that differ only by case (i.e., one uppercase and one lowercase of the same letter). The solution has been implemented in C++ and utilizes a vector<char> as the stack for easy push and pop operations.

Understand the following key points in the code:

  • A vector<char> charStack is created to function as a stack.
  • A loop processes each character of the input string s.
  • Within the loop, for each character, the code checks:
    • If charStack is not empty and the difference between the ASCII values of charStack.back() (the current top element of the stack) and the character ch is 32 (which occurs when the characters are the same letter but differing in case), it pops the character from the stack.
    • If the condition is not met, the character is pushed onto the stack.
  • After the loop, the characters remaining in charStack represent the processed string. These characters are then used to construct the resulting string.

This approach ensures that whenever adjacent characters are of the same type but different cases, they cancel each other out, creating a reduced or "good" string without adjacent characters that are of the same type but different cases. The use of a vector simplifies the push and pop operations and makes the code compact and efficient.

java
class Solution {
    public String simplifyString(String input) {
        // Initializing a stack to manage character checks
        Stack<Character> characters = new Stack<>();
        
        // Process each character in the input string
        for (char ch : input.toCharArray()) {
            // Check condition for character removal (case insensitive match)
            if (!characters.isEmpty() && Math.abs(characters.peek() - ch) == 32) {
                characters.pop();
            } else {
                characters.push(ch);
            }
        }
        
        // Build final string from characters left in stack
        StringBuilder result = new StringBuilder();
        for (char element : characters) {
            result.append(element);
        }
        return result.toString();
    }
}

The Java solution provided addresses the problem of reducing a string by removing adjacent letter pairs that differ only in case. To achieve this, the solution utilizes a stack data structure to keep track of characters that might need to be removed based on given conditions.

  • The simplifyString method takes an input string and processes each character:
    1. Convert the input string into a character array to iterate over each character.
    2. Initialize a Stack<Character> to store and manage characters.
    3. For each character in the array, determine if the character on top of the stack is the same letter but of opposite case. This is checked using Math.abs(characters.peek() - ch) == 32, which compares ASCII values to determine if they are the same letter in different cases (uppercase and lowercase).
    4. If the condition is true, pop the stack's top character (remove that pair from consideration). If it's false, push the current character onto the stack.
    5. After processing all characters, construct the resultant string using characters left in the stack. This is done by iterating through the stack and appending each character to a StringBuilder.
    6. Return the final formed string, which is the simplified version of the input string.

This approach efficiently handles the pair removal operation with an optimal O(n) time complexity, ensuring that each character is processed once. The use of the stack allows for effective pair matching and removal inline with the requirements of simplifying the string based on case-insensitive character pairs.

python
class Solution:
    def simplifyString(self, input_str: str) -> str:
        result_stack = []
        
        for character in input_str:
            if result_stack and abs(ord(character) - ord(result_stack[-1])) == 32:
                result_stack.pop()
            else:
                result_stack.append(character)
                
        return "".join(result_stack)

This solution focuses on simplifying an input string by removing pairs of adjacent characters that differ only in case. Implement this in Python by defining a method, simplifyString, which uses a stack to efficiently process and modify the string as follows:

  • Initialize an empty list, result_stack, to act as a stack.
  • Iterate over each character in the input string.
  • For each character, check if the stack is not empty and if the absolute difference between the ASCII values of this character and the last character in the stack is 32 (indicating differing cases of the same letter). If true, pop the last character from the stack – this effectively removes the case-sensitive adjacent pair.
  • If the condition is not met, append the current character to the stack.
  • After processing all characters, convert the stack back to a string by joining all elements in the stack.

This approach ensures only non-adjacent case-insensitive characters remain, simplifying the input string according to the given criteria. Adjust as needed to treat other string manipulation cases or to optimize performance for very large strings.

Comments

No comments yet.