3856. Trim Trailing Vowels
Problem Description
You are given a string s made up of lowercase English letters.
Your task is to remove all trailing vowels from s and return the resulting string.
The vowels are the characters 'a', 'e', 'i', 'o', and 'u'.
In other words, starting from the end of the string, you keep deleting characters as long as they are vowels. Once you reach a character that is not a vowel (a consonant), you stop, and return the portion of the string from the beginning up to and including that character.
For example, if s = "leetcode", the last character 'e' is a vowel, so it gets removed. The next character from the end is 'd', which is a consonant, so the trimming stops. The result is "leetcod".
If every character in the string is a vowel, the result is an empty string.
How We Pick the Algorithm
Why Simulation / Basic DSA?
This problem maps to Simulation / Basic DSA through a short path in the full flowchart.
Scan backwards from the end removing vowels until a consonant is found, a simple linear traversal with no algorithmic pattern needed.
Open in FlowchartIntuition
The key observation is that we only care about the trailing vowels — the vowels that appear at the very end of the string. The characters in the middle or at the beginning do not matter, even if they are vowels.
Since we want to remove characters from the end, it makes sense to look at the string starting from its last character and move backwards. As long as the character we are looking at is a vowel, it belongs to the trailing run of vowels and should be removed. The moment we hit a character that is not a vowel, we know the trailing vowels have ended, so we stop.
At that point, the position of the first non-vowel character (scanning from the right) tells us exactly where the answer should end. Everything from the start of the string up to and including that character is the part we want to keep, and everything after it was a trailing vowel.
This naturally leads us to use a pointer i starting at the last index and decreasing it while s[i] is a vowel. When the loop stops, s[: i + 1] gives the string with all trailing vowels removed. If the entire string consists of vowels, the pointer moves past the beginning and i + 1 becomes 0, correctly returning an empty string.
Solution Approach
Solution 1: Reverse Traversal
We traverse the string from the end in reverse order until we encounter the first non-vowel character. Then we return the substring from the beginning of the string up to that position.
The implementation follows these steps:
-
Initialize a pointer. We set
i = len(s) - 1, so thatipoints to the last character of the string. This pointer will move leftward as we skip over trailing vowels. -
Scan backwards while skipping vowels. We run a loop with two conditions:
i >= 0ensures we stay within the bounds of the string and do not go past the beginning.s[i] in "aeiou"checks whether the current character is a vowel.
As long as both conditions hold, we decrement
iby1, effectively moving past each trailing vowel. The loop stops as soon as we either reach a non-vowel character or move past the start of the string. -
Return the trimmed string. After the loop ends,
ipoints to the last non-vowel character (or to-1if the whole string is made of vowels). We returns[: i + 1], which is the substring from the beginning up to and including indexi. The slices[: i + 1]naturally handles the empty-string case: wheni = -1, it producess[:0], which is"".
This approach uses a simple two-pointer / single-pointer scan pattern, where one pointer walks from the right toward the left. No extra data structures are needed, and we only look at each trailing vowel once.
The time complexity is O(n), where n is the length of the string, since in the worst case (an all-vowel string) we examine every character. The space complexity is O(1) for the pointer, not counting the space used by the returned substring.
Example Walkthrough
Let's trace through the algorithm using the example s = "consume".
The string has 7 characters, indexed as follows:
| Index | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| Char | c | o | n | s | u | m | e |
Step 1: Initialize the pointer.
We set i = len(s) - 1 = 6, so i starts at the last character 'e'.
Step 2: Scan backwards while skipping vowels.
i = 6:s[6] = 'e'. Since'e'is a vowel, we decrementito5.i = 5:s[5] = 'm'. Since'm'is not a vowel (it's a consonant), the loop stops here.
The pointer settles at i = 5, which points to the last non-vowel character 'm'.
Step 3: Return the trimmed string.
We return s[: i + 1] = s[: 6] = "consum".
Notice that only the single trailing 'e' was removed. The vowels 'o' (index 1) and 'u' (index 4) sit in the middle of the string, so they are left untouched — exactly as intended, since we only care about the trailing run of vowels.
Edge case check — all vowels:
If instead s = "aeiou", the pointer would decrement past every character: 4 → 3 → 2 → 1 → 0 → -1. When the loop exits with i = -1, the slice s[: -1 + 1] = s[: 0] produces "", correctly returning the empty string.
Solution Implementation
1class Solution:
2 def trimTrailingVowels(self, s: str) -> str:
3 # Define the set of vowels for quick membership lookup
4 vowels = set("aeiou")
5
6 # Start scanning from the last character of the string
7 index = len(s) - 1
8
9 # Move the pointer leftward while the current character is a vowel
10 while index >= 0 and s[index] in vowels:
11 index -= 1
12
13 # Return the substring up to (and including) the last non-vowel character
14 return s[: index + 1]
151class Solution {
2 /**
3 * Removes all trailing vowels (a, e, i, o, u) from the end of the given string.
4 *
5 * @param s the input string to be processed
6 * @return the string with trailing vowels removed
7 */
8 public String trimTrailingVowels(String s) {
9 // Define the set of vowel characters to check against.
10 final String vowels = "aeiou";
11
12 // Start scanning from the last character of the string.
13 int index = s.length() - 1;
14
15 // Move the index leftward as long as the current character is a vowel.
16 while (index >= 0 && vowels.indexOf(s.charAt(index)) != -1) {
17 index--;
18 }
19
20 // Return the substring from the start up to and including the last non-vowel character.
21 return s.substring(0, index + 1);
22 }
23}
241class Solution {
2public:
3 string trimTrailingVowels(string s) {
4 // Set of vowel characters used for lookup.
5 const string vowels = "aeiou";
6
7 // Start scanning from the last character of the string.
8 int index = static_cast<int>(s.size()) - 1;
9
10 // Move the index backward while the current character is a vowel.
11 // string::npos indicates the character was NOT found in 'vowels',
12 // so the loop continues only when a vowel is found.
13 while (index >= 0 && vowels.find(s[index]) != string::npos) {
14 --index;
15 }
16
17 // Return the substring from the beginning up to (and including)
18 // the last non-vowel character. If all characters were vowels,
19 // index becomes -1 and an empty string is returned.
20 return s.substr(0, index + 1);
21 }
22};
231/**
2 * Removes all trailing vowels (a, e, i, o, u) from the given string.
3 *
4 * @param s - The input string to be trimmed.
5 * @returns A new string with any trailing vowels removed.
6 */
7function trimTrailingVowels(s: string): string {
8 // Define the set of vowels to check against.
9 const vowels = 'aeiou';
10
11 // Start from the last character of the string.
12 let index = s.length - 1;
13
14 // Move the index backwards while the current character is a vowel.
15 while (index >= 0 && vowels.indexOf(s[index]) !== -1) {
16 index--;
17 }
18
19 // Return the substring from the start up to (and including) the last
20 // non-vowel character. If all characters are vowels, this yields an empty string.
21 return s.slice(0, index + 1);
22}
23Time and Space Complexity
-
Time complexity:
O(n), wherenis the length of the strings. In the worst case (e.g., when all characters are vowels), thewhileloop traverses the entire string from the end to the beginning, performing one comparison per character. The slicing operations[: i + 1]also takesO(n)time in the worst case. Therefore, the overall time complexity isO(n). -
Space complexity:
O(1). Only a constant amount of extra space is used for the index variablei. Although the slicing operation creates a new string, this is typically considered as the output rather than auxiliary space, so the additional space usage isO(1).
Pattern Learn more about how to find time and space complexity quickly.
Common Pitfalls
Pitfall 1: Only removing a single trailing vowel instead of all of them
A very common mistake is to check just the last character once, remove it if it's a vowel, and then return immediately. This handles the simplest case but fails whenever there are multiple consecutive trailing vowels.
# WRONG: only strips one vowel
class Solution:
def trimTrailingVowels(self, s: str) -> str:
if s and s[-1] in "aeiou":
return s[:-1]
return s
For an input like s = "queue", the correct result is "q" (the trailing "ueue" are all vowels), but this code returns "queu". The fix is to use a loop that keeps moving the pointer leftward until a consonant is found, exactly as the reference solution does:
index = len(s) - 1
while index >= 0 and s[index] in vowels:
index -= 1
return s[: index + 1]
Pitfall 2: Off-by-one error in the returned slice
After the loop ends, index points to the last non-vowel character. A frequent error is returning s[:index] instead of s[:index + 1], which accidentally drops the final consonant.
# WRONG: chops off the last valid consonant return s[:index] # for "leetcode" this yields "leetco" instead of "leetcod"
Remember that Python slicing is end-exclusive, so to include the character at index you must slice up to index + 1.
Pitfall 3: Mishandling the all-vowel (and empty) string case
When every character is a vowel (e.g., s = "aeiou"), the loop decrements index all the way down to -1. Some implementations attempt to special-case this or forget to guard the boundary, leading to an IndexError or an incorrect result.
# WRONG: missing the index >= 0 guard causes negative-index wrap-around while s[index] in vowels: # when index reaches -1, s[-1] wraps to the end! index -= 1
Without the index >= 0 condition, once index becomes -1, s[-1] wraps around to the last character of the string, producing an infinite loop or incorrect behavior. The reference solution handles this gracefully:
- The condition
index >= 0stops the loop at the boundary. - The slice
s[: index + 1]becomess[:0], which correctly returns"".
Always check the bounds before indexing, and trust that the end-exclusive slice naturally produces an empty string for the all-vowel case.
Ready to land your dream job?
Unlock your dream job with a 5-minute quiz for a personalized study roadmap!
Get My RoadmapWhat's the relationship between a tree and a graph?
Recommended Readings
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Recursion If you prefer videos here's a video that explains recursion in a fun and easy way Recursion is one of the most important concepts in computer science Simply speaking recursion is the process of a function calling itself Using a real life analogy imagine a scenario where you invite your friends to lunch https assets algo monster recursion jpg You first call Ben and ask him
Runtime Overview When learning about algorithms and data structures you'll frequently encounter the term time complexity This concept is fundamental in computer science and offers insights into how long an algorithm takes to complete given a certain input size What is Time Complexity Time complexity describes how the time needed
Want a Structured Path to Master System Design Too? Don’t Miss This!