2785. Sort Vowels in a String
Problem Description
You are given a 0-indexed string s
. Your task is to rearrange (permute) the characters in s
to create a new string t
with the following rules:
-
Consonants stay in place: Any character that is a consonant must remain at its original position. If
s[i]
is a consonant, thent[i] = s[i]
. -
Vowels are sorted: All vowels in the string must be rearranged so they appear in non-decreasing order by their ASCII values. The vowels should fill the positions where vowels originally existed in
s
, but now sorted from smallest to largest ASCII value.
The vowels are defined as 'a'
, 'e'
, 'i'
, 'o'
, and 'u'
(both lowercase and uppercase versions). All other letters are consonants.
For example, if you have a string with vowels at positions 1, 3, and 5, you would:
- Extract all the vowels from the string
- Sort these vowels by their ASCII values
- Place the sorted vowels back into positions 1, 3, and 5 (where vowels originally were)
- Keep all consonants in their original positions
The solution approach involves:
- Collecting all vowels from the string into a separate list
vs
- Sorting this list of vowels
- Creating a copy of the original string as a list
- Iterating through the string and replacing each vowel position with the next vowel from the sorted list
- Joining the result back into a string
Intuition
The key insight is that we need to handle vowels and consonants separately. Since consonants must remain in their original positions, we can think of them as "fixed" elements. The vowels, on the other hand, need to be rearranged among themselves.
When we need to sort only specific elements while keeping others fixed, a natural approach is to:
- Extract the elements that need sorting
- Sort them independently
- Put them back in their designated positions
Think of it like having a bookshelf where some books (consonants) are glued in place, while others (vowels) can be moved. To sort the moveable books alphabetically, we would:
- Take out all the moveable books
- Sort them on a table
- Put them back into the empty slots in order
The ASCII value sorting requirement tells us we need to consider both uppercase and lowercase letters. Since uppercase letters have smaller ASCII values than lowercase ones (e.g., 'A'
= 65 < 'a'
= 97), when we sort the vowels, uppercase vowels will naturally come before lowercase ones.
This leads us to a two-pass solution:
- First pass: collect all vowels and sort them
- Second pass: traverse the string again, replacing vowels with sorted ones in order while keeping consonants unchanged
The beauty of this approach is its simplicity - we don't need complex in-place swapping or tracking multiple pointers. We just extract, sort, and replace.
Learn more about Sorting patterns.
Solution Approach
The implementation follows a straightforward extract-sort-replace pattern:
Step 1: Extract Vowels
vs = [c for c in s if c.lower() in "aeiou"]
We use a list comprehension to iterate through the string s
and collect all vowels. The condition c.lower() in "aeiou"
cleverly handles both uppercase and lowercase vowels by converting each character to lowercase for the check, but storing the original character in the list.
Step 2: Sort the Vowels
vs.sort()
We sort the vowels list in-place. Python's sort uses ASCII values by default, so uppercase letters (ASCII 65-90) will come before lowercase letters (ASCII 97-122). For example, ['e', 'A', 'o']
becomes ['A', 'e', 'o']
.
Step 3: Create a Mutable Copy
cs = list(s)
Since strings are immutable in Python, we convert the string to a list of characters that we can modify.
Step 4: Replace Vowels in Order
j = 0
for i, c in enumerate(cs):
if c.lower() in "aeiou":
cs[i] = vs[j]
j += 1
We traverse the character list with index i
. When we encounter a vowel (checked the same way as in Step 1), we replace it with the next sorted vowel from vs
using pointer j
. The pointer j
ensures we use sorted vowels in order. Consonants remain untouched as we only modify positions where vowels exist.
Step 5: Build the Result
return "".join(cs)
Finally, we join the character list back into a string and return it.
The time complexity is O(n log n)
due to sorting the vowels, where n
is the length of the string. The space complexity is O(n)
for storing the vowels list and the character list.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through the solution with the string s = "lEetcOde"
.
Step 1: Extract Vowels We identify and collect all vowels from the string:
- Position 1: 'E' (vowel)
- Position 2: 'e' (vowel)
- Position 5: 'O' (vowel)
- Position 6: 'd' (consonant)
- Position 7: 'e' (vowel)
Extracted vowels: vs = ['E', 'e', 'O', 'e']
Step 2: Sort the Vowels Sort by ASCII values:
- 'E' (ASCII 69) < 'O' (ASCII 79) < 'e' (ASCII 101) < 'e' (ASCII 101)
Sorted vowels: vs = ['E', 'O', 'e', 'e']
Step 3: Create a Mutable Copy
Convert string to list: cs = ['l', 'E', 'e', 't', 'c', 'O', 'd', 'e']
Step 4: Replace Vowels in Order Traverse the list and replace vowels with sorted ones:
- i=0: 'l' is consonant β keep 'l'
- i=1: 'E' is vowel β replace with vs[0]='E' β
['l', 'E', 'e', 't', 'c', 'O', 'd', 'e']
- i=2: 'e' is vowel β replace with vs[1]='O' β
['l', 'E', 'O', 't', 'c', 'O', 'd', 'e']
- i=3: 't' is consonant β keep 't'
- i=4: 'c' is consonant β keep 'c'
- i=5: 'O' is vowel β replace with vs[2]='e' β
['l', 'E', 'O', 't', 'c', 'e', 'd', 'e']
- i=6: 'd' is consonant β keep 'd'
- i=7: 'e' is vowel β replace with vs[3]='e' β
['l', 'E', 'O', 't', 'c', 'e', 'd', 'e']
Step 5: Build the Result
Join the characters: "lEOtcede"
The consonants 'l', 't', 'c', 'd' stayed in positions 0, 3, 4, 6 respectively, while the vowels were rearranged in sorted order across positions 1, 2, 5, 7.
Solution Implementation
1class Solution:
2 def sortVowels(self, s: str) -> str:
3 # Extract all vowels (both uppercase and lowercase) from the string
4 vowels = [char for char in s if char.lower() in "aeiou"]
5
6 # Sort the vowels in ascending order (uppercase letters come before lowercase)
7 vowels.sort()
8
9 # Convert string to list for character replacement
10 characters = list(s)
11
12 # Index to track position in sorted vowels list
13 vowel_index = 0
14
15 # Replace each vowel in the original string with sorted vowels
16 for i, char in enumerate(characters):
17 if char.lower() in "aeiou":
18 characters[i] = vowels[vowel_index]
19 vowel_index += 1
20
21 # Join the list back into a string and return
22 return "".join(characters)
23
1class Solution {
2 /**
3 * Sorts the vowels in a string while keeping consonants in their original positions.
4 * Vowels are sorted in ascending order based on ASCII values.
5 *
6 * @param s the input string to process
7 * @return a new string with vowels sorted and consonants unchanged
8 */
9 public String sortVowels(String s) {
10 // List to store all vowels found in the string
11 List<Character> vowelsList = new ArrayList<>();
12
13 // Convert string to character array for easier manipulation
14 char[] charArray = s.toCharArray();
15
16 // First pass: Extract all vowels from the string
17 for (char currentChar : charArray) {
18 // Convert to lowercase for vowel checking
19 char lowerCaseChar = Character.toLowerCase(currentChar);
20
21 // Check if the character is a vowel
22 if (lowerCaseChar == 'a' || lowerCaseChar == 'e' ||
23 lowerCaseChar == 'i' || lowerCaseChar == 'o' ||
24 lowerCaseChar == 'u') {
25 // Add the original character (preserving case) to vowels list
26 vowelsList.add(currentChar);
27 }
28 }
29
30 // Sort the vowels in ascending order (based on ASCII values)
31 Collections.sort(vowelsList);
32
33 // Second pass: Replace vowels in original positions with sorted vowels
34 int vowelIndex = 0; // Index to track position in sorted vowels list
35
36 for (int i = 0; i < charArray.length; i++) {
37 // Convert to lowercase for vowel checking
38 char lowerCaseChar = Character.toLowerCase(charArray[i]);
39
40 // If current position contains a vowel, replace with sorted vowel
41 if (lowerCaseChar == 'a' || lowerCaseChar == 'e' ||
42 lowerCaseChar == 'i' || lowerCaseChar == 'o' ||
43 lowerCaseChar == 'u') {
44 // Replace with the next sorted vowel
45 charArray[i] = vowelsList.get(vowelIndex);
46 vowelIndex++;
47 }
48 }
49
50 // Convert character array back to string and return
51 return String.valueOf(charArray);
52 }
53}
54
1class Solution {
2public:
3 string sortVowels(string s) {
4 // Extract all vowels from the string
5 string vowels;
6 for (char c : s) {
7 char lowerChar = tolower(c);
8 // Check if the character is a vowel (case-insensitive check)
9 if (lowerChar == 'a' || lowerChar == 'e' || lowerChar == 'i' ||
10 lowerChar == 'o' || lowerChar == 'u') {
11 vowels.push_back(c);
12 }
13 }
14
15 // Sort the extracted vowels in ascending order
16 ranges::sort(vowels);
17
18 // Replace vowels in original string with sorted vowels
19 int vowelIndex = 0;
20 for (int i = 0; i < s.size(); ++i) {
21 char lowerChar = tolower(s[i]);
22 // If current position is a vowel, replace with sorted vowel
23 if (lowerChar == 'a' || lowerChar == 'e' || lowerChar == 'i' ||
24 lowerChar == 'o' || lowerChar == 'u') {
25 s[i] = vowels[vowelIndex++];
26 }
27 }
28
29 return s;
30 }
31};
32
1/**
2 * Sorts vowels in a string while keeping consonants in their original positions
3 * @param s - The input string to process
4 * @returns A new string with vowels sorted in ascending order
5 */
6function sortVowels(s: string): string {
7 // Define all vowels (both lowercase and uppercase)
8 const vowelSet: string[] = ['a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'];
9
10 // Extract and sort all vowels from the input string
11 const sortedVowels: string[] = s
12 .split('')
13 .filter((char: string) => vowelSet.includes(char))
14 .sort();
15
16 // Build the result string
17 const result: string[] = [];
18 let vowelIndex: number = 0;
19
20 // Iterate through each character in the original string
21 for (const char of s) {
22 // If current character is a vowel, replace with sorted vowel
23 // Otherwise, keep the original character
24 if (vowelSet.includes(char)) {
25 result.push(sortedVowels[vowelIndex]);
26 vowelIndex++;
27 } else {
28 result.push(char);
29 }
30 }
31
32 // Join the result array into a string and return
33 return result.join('');
34}
35
Time and Space Complexity
Time Complexity: O(n Γ log n)
The algorithm consists of the following steps:
- Extract vowels from the string by iterating through all
n
characters -O(n)
- Sort the extracted vowels using Python's built-in sort -
O(k Γ log k)
wherek
is the number of vowels, and in the worst casek = n
, so this becomesO(n Γ log n)
- Convert the original string to a list -
O(n)
- Iterate through the list and replace vowels with sorted vowels -
O(n)
- Join the list back into a string -
O(n)
The dominant operation is the sorting step with O(n Γ log n)
complexity, making the overall time complexity O(n Γ log n)
.
Space Complexity: O(n)
The algorithm uses additional space for:
- List
vs
to store vowels - in the worst case, all characters are vowels, soO(n)
- List
cs
which is a copy of the original string -O(n)
- The output string created by join -
O(n)
Therefore, the total space complexity is O(n)
, where n
is the length of the input string s
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Incorrectly Handling Case Sensitivity
A common mistake is forgetting that uppercase and lowercase vowels have different ASCII values, with uppercase letters having smaller ASCII values than lowercase ones.
Pitfall Example:
# Incorrect: This would sort alphabetically ignoring case
vowels.sort(key=str.lower) # 'A' and 'a' would be treated as equal
Why it's wrong: The problem specifically asks to sort by ASCII values. 'A' (ASCII 65) should come before 'a' (ASCII 97), but using key=str.lower
would treat them as equivalent and maintain their original relative order.
Correct Approach:
vowels.sort() # Default sort uses ASCII values
2. Modifying the String Directly
Attempting to modify the string in-place without converting it to a mutable data structure.
Pitfall Example:
# Incorrect: Strings are immutable in Python
for i in range(len(s)):
if s[i].lower() in "aeiou":
s[i] = vowels[vowel_index] # TypeError: 'str' object does not support item assignment
Correct Approach:
characters = list(s) # Convert to mutable list first
for i in range(len(characters)):
if characters[i].lower() in "aeiou":
characters[i] = vowels[vowel_index]
3. Using Set for Vowel Check Without Considering Performance
While using a set for vowel checking seems more efficient, for small fixed sets like vowels, the string membership test is actually comparable or faster.
Alternative (not necessarily better):
VOWELS = set('aeiouAEIOU')
if char in VOWELS: # Slightly different approach
Why the original is fine: For a 10-character string like "aeiouAEIOU", Python's string membership test is optimized and performs well. The char.lower() in "aeiou"
approach is also more readable and handles both cases elegantly.
4. Forgetting to Track the Vowel Index
Missing or incorrectly updating the index for the sorted vowels list.
Pitfall Example:
# Incorrect: Using the same index for both arrays
for i in range(len(characters)):
if characters[i].lower() in "aeiou":
characters[i] = vowels[i] # Wrong! Should use separate index
Correct Approach:
vowel_index = 0
for i in range(len(characters)):
if characters[i].lower() in "aeiou":
characters[i] = vowels[vowel_index]
vowel_index += 1 # Don't forget to increment!
Which of these properties could exist for a graph but not a tree?
Recommended Readings
Sorting Summary Comparisons We presented quite a few sorting algorithms and it is essential to know the advantages and disadvantages of each one The basic algorithms are easy to visualize and easy to learn for beginner programmers because of their simplicity As such they will suffice if you don't know any advanced
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 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
Want a Structured Path to Master System Design Too? Donβt Miss This!