Facebook Pixel

1839. Longest Substring Of All Vowels in Order

Problem Description

You need to find the longest "beautiful" substring in a string that contains only vowels ('a', 'e', 'i', 'o', 'u').

A substring is considered beautiful if it meets two conditions:

  1. It contains all 5 vowels at least once
  2. The vowels appear in alphabetical order (all 'a's come before 'e's, all 'e's come before 'i's, and so on)

For example:

  • "aeiou" is beautiful - has all vowels in order
  • "aaaaaaeiiiioou" is beautiful - has all vowels in order with repetitions allowed
  • "uaeio" is NOT beautiful - vowels are not in alphabetical order
  • "aeoiu" is NOT beautiful - 'o' comes before 'i', breaking alphabetical order
  • "aaaeeeooo" is NOT beautiful - missing vowels 'i' and 'u'

Given a string word consisting only of English vowels, return the length of the longest beautiful substring. If no beautiful substring exists, return 0.

The solution approach groups consecutive identical vowels together into (vowel, count) pairs. For instance, "aaaeiouu" becomes [('a', 3), ('e', 1), ('i', 1), ('o', 1), ('u', 2)]. Then it checks every sequence of 5 consecutive groups to see if they form the pattern "aeiou" in order. If they do, the sum of their counts gives the length of that beautiful substring. The algorithm tracks the maximum length found.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

Intuition

The key insight is that a beautiful substring must have exactly 5 distinct segments of vowels appearing in the specific order 'a', 'e', 'i', 'o', 'u'. Each segment can have one or more of the same vowel repeated consecutively.

Think about what a beautiful substring looks like: it could be "aeiou" (minimal case) or "aaaeeeiiioooouuuu" (with repetitions). Notice that we always have groups of consecutive identical vowels, and these groups must follow the alphabetical pattern.

This observation leads us to transform the problem. Instead of checking every possible substring character by character, we can group consecutive identical vowels together. For example, "aaaeiouu" becomes a sequence of groups: [('a', 3), ('e', 1), ('i', 1), ('o', 1), ('u', 2)].

Once we have this grouped representation, finding a beautiful substring becomes much simpler - we just need to find sequences of exactly 5 consecutive groups where the vowels are 'a', 'e', 'i', 'o', 'u' in that order. The length of such a beautiful substring is simply the sum of counts in these 5 groups.

Why does this work? Because any beautiful substring must transition from 'a' to 'e' to 'i' to 'o' to 'u' exactly once (no going back), and within each vowel section, we can have any number of repetitions. By grouping consecutive vowels, we capture this structure perfectly and can check the pattern efficiently by looking at sequences of 5 groups at a time.

Learn more about Sliding Window patterns.

Solution Approach

The implementation follows a two-phase approach: transformation and validation.

Phase 1: Transform the string into groups

We use two pointers to group consecutive identical vowels. Starting with pointer i at position 0:

  • Set pointer j = i
  • Move j forward while word[j] == word[i] (same vowel)
  • Create a tuple (word[i], j - i) where the first element is the vowel and the second is its count
  • Move i to j and repeat until we've processed the entire string

For example, "aaaeiouu" transforms into arr = [('a', 3), ('e', 1), ('i', 1), ('o', 1), ('u', 2)].

Phase 2: Find beautiful substrings

We iterate through the array arr and examine every sequence of 5 consecutive groups:

  • For position i, extract 5 groups: a, b, c, d, e = arr[i : i + 5]
  • Check if the vowels form the pattern "aeiou" by concatenating the first elements: a[0] + b[0] + c[0] + d[0] + e[0] == "aeiou"
  • If the pattern matches, calculate the total length: a[1] + b[1] + c[1] + d[1] + e[1]
  • Track the maximum length found

The loop runs from i = 0 to len(arr) - 4 to ensure we always have 5 groups to examine.

Why this works:

  • Any beautiful substring must contain exactly 5 distinct vowel segments in alphabetical order
  • By grouping consecutive vowels, we reduce the problem to pattern matching on groups
  • A sliding window of size 5 on the groups array captures all possible beautiful substrings
  • The time complexity is O(n) for transformation plus O(m) for validation where m is the number of groups (m ≤ n)

Ready to land your dream job?

Unlock your dream job with a 5-minute evaluator for a personalized learning plan!

Start Evaluator

Example Walkthrough

Let's walk through the solution with the string word = "aaeiiouuuaaaeeiou".

Step 1: Transform into groups

We scan through the string and group consecutive identical vowels:

  • Start at index 0: 'a' appears twice → ('a', 2)
  • Move to index 2: 'e' appears once → ('e', 1)
  • Move to index 3: 'i' appears twice → ('i', 2)
  • Move to index 5: 'o' appears once → ('o', 1)
  • Move to index 6: 'u' appears three times → ('u', 3)
  • Move to index 9: 'a' appears three times → ('a', 3)
  • Move to index 12: 'e' appears twice → ('e', 2)
  • Move to index 14: 'i' appears once → ('i', 1)
  • Move to index 15: 'o' appears once → ('o', 1)
  • Move to index 16: 'u' appears once → ('u', 1)

Result: arr = [('a', 2), ('e', 1), ('i', 2), ('o', 1), ('u', 3), ('a', 3), ('e', 2), ('i', 1), ('o', 1), ('u', 1)]

Step 2: Check sequences of 5 groups

Now we slide a window of size 5 across the array:

  • Window at index 0-4: [('a', 2), ('e', 1), ('i', 2), ('o', 1), ('u', 3)]

    • Vowels: 'a' + 'e' + 'i' + 'o' + 'u' = "aeiou"
    • This is beautiful! Length = 2 + 1 + 2 + 1 + 3 = 9
    • Update max_length = 9
  • Window at index 1-5: [('e', 1), ('i', 2), ('o', 1), ('u', 3), ('a', 3)]

    • Vowels: 'e' + 'i' + 'o' + 'u' + 'a' = "eioua"
    • Not the pattern "aeiou", skip
  • Window at index 2-6: [('i', 2), ('o', 1), ('u', 3), ('a', 3), ('e', 2)]

    • Vowels: 'i' + 'o' + 'u' + 'a' + 'e' = "iouae"
    • Not the pattern "aeiou", skip
  • Continue sliding...

  • Window at index 5-9: [('a', 3), ('e', 2), ('i', 1), ('o', 1), ('u', 1)]

    • Vowels: 'a' + 'e' + 'i' + 'o' + 'u' = "aeiou"
    • This is beautiful! Length = 3 + 2 + 1 + 1 + 1 = 8
    • max_length remains 9 (since 9 > 8)

Result: The longest beautiful substring has length 9, corresponding to the substring "aaeiiouu" from the original string.

Solution Implementation

1class Solution:
2    def longestBeautifulSubstring(self, word: str) -> int:
3        # Group consecutive identical characters into (character, count) pairs
4        grouped_chars = []
5        n = len(word)
6        i = 0
7      
8        # Build array of (character, frequency) tuples
9        while i < n:
10            j = i
11            # Count consecutive occurrences of the same character
12            while j < n and word[j] == word[i]:
13                j += 1
14            grouped_chars.append((word[i], j - i))
15            i = j
16      
17        max_length = 0
18      
19        # Check each window of 5 consecutive groups
20        for i in range(len(grouped_chars) - 4):
21            # Extract 5 consecutive groups
22            group_a, group_b, group_c, group_d, group_e = grouped_chars[i:i + 5]
23          
24            # Check if the characters form "aeiou" in order
25            if (group_a[0] + group_b[0] + group_c[0] + 
26                group_d[0] + group_e[0] == "aeiou"):
27                # Calculate total length of this beautiful substring
28                current_length = (group_a[1] + group_b[1] + group_c[1] + 
29                                 group_d[1] + group_e[1])
30                max_length = max(max_length, current_length)
31      
32        return max_length
33
1class Solution {
2    /**
3     * Finds the length of the longest beautiful substring.
4     * A beautiful substring must contain all vowels (a, e, i, o, u) in alphabetical order.
5     * 
6     * @param word the input string to search for beautiful substrings
7     * @return the length of the longest beautiful substring, or 0 if none exists
8     */
9    public int longestBeautifulSubstring(String word) {
10        int wordLength = word.length();
11      
12        // Group consecutive identical characters into segments
13        List<CharacterSegment> segments = new ArrayList<>();
14      
15        // Build segments by grouping consecutive identical characters
16        for (int startIndex = 0; startIndex < wordLength;) {
17            int endIndex = startIndex;
18          
19            // Find the end of current segment with same character
20            while (endIndex < wordLength && word.charAt(endIndex) == word.charAt(startIndex)) {
21                endIndex++;
22            }
23          
24            // Create segment with character and its count
25            segments.add(new CharacterSegment(word.charAt(startIndex), endIndex - startIndex));
26            startIndex = endIndex;
27        }
28      
29        int maxBeautifulLength = 0;
30      
31        // Check each possible sequence of 5 consecutive segments
32        for (int i = 0; i < segments.size() - 4; i++) {
33            // Get 5 consecutive segments
34            CharacterSegment firstSegment = segments.get(i);
35            CharacterSegment secondSegment = segments.get(i + 1);
36            CharacterSegment thirdSegment = segments.get(i + 2);
37            CharacterSegment fourthSegment = segments.get(i + 3);
38            CharacterSegment fifthSegment = segments.get(i + 4);
39          
40            // Check if segments form a beautiful pattern (a, e, i, o, u)
41            if (firstSegment.character == 'a' && 
42                secondSegment.character == 'e' && 
43                thirdSegment.character == 'i' && 
44                fourthSegment.character == 'o' && 
45                fifthSegment.character == 'u') {
46              
47                // Calculate total length of this beautiful substring
48                int currentLength = firstSegment.count + secondSegment.count + 
49                                  thirdSegment.count + fourthSegment.count + 
50                                  fifthSegment.count;
51              
52                // Update maximum length if current is longer
53                maxBeautifulLength = Math.max(maxBeautifulLength, currentLength);
54            }
55        }
56      
57        return maxBeautifulLength;
58    }
59}
60
61/**
62 * Represents a segment of consecutive identical characters in a string.
63 */
64class CharacterSegment {
65    char character;  // The character in this segment
66    int count;       // The number of consecutive occurrences
67  
68    /**
69     * Constructs a character segment.
70     * 
71     * @param character the character that appears consecutively
72     * @param count the number of consecutive occurrences
73     */
74    CharacterSegment(char character, int count) {
75        this.character = character;
76        this.count = count;
77    }
78}
79
1class Solution {
2public:
3    int longestBeautifulSubstring(string word) {
4        // Group consecutive identical characters into pairs of (character, count)
5        vector<pair<char, int>> groups;
6        int n = word.size();
7      
8        // Build the groups array by counting consecutive identical characters
9        for (int i = 0; i < n;) {
10            int j = i;
11            // Count consecutive occurrences of the same character
12            while (j < n && word[j] == word[i]) {
13                ++j;
14            }
15            // Store the character and its count
16            groups.push_back({word[i], j - i});
17            i = j;
18        }
19      
20        int maxLength = 0;
21      
22        // Check each possible sequence of 5 consecutive groups
23        // We need exactly 5 groups in order: 'a', 'e', 'i', 'o', 'u'
24        for (int i = 0; i < (int)groups.size() - 4; ++i) {
25            // Extract the 5 consecutive groups
26            auto& [char1, count1] = groups[i];
27            auto& [char2, count2] = groups[i + 1];
28            auto& [char3, count3] = groups[i + 2];
29            auto& [char4, count4] = groups[i + 3];
30            auto& [char5, count5] = groups[i + 4];
31          
32            // Check if the sequence forms 'aeiou' in order
33            if (char1 == 'a' && char2 == 'e' && char3 == 'i' && 
34                char4 == 'o' && char5 == 'u') {
35                // Update maximum length with the total count of this beautiful substring
36                maxLength = max(maxLength, count1 + count2 + count3 + count4 + count5);
37            }
38        }
39      
40        return maxLength;
41    }
42};
43
1function longestBeautifulSubstring(word: string): number {
2    // Group consecutive identical characters into pairs of [character, count]
3    const groups: Array<[string, number]> = [];
4    const n = word.length;
5  
6    // Build the groups array by counting consecutive identical characters
7    let i = 0;
8    while (i < n) {
9        let j = i;
10        // Count consecutive occurrences of the same character
11        while (j < n && word[j] === word[i]) {
12            j++;
13        }
14        // Store the character and its count
15        groups.push([word[i], j - i]);
16        i = j;
17    }
18  
19    let maxLength = 0;
20  
21    // Check each possible sequence of 5 consecutive groups
22    // We need exactly 5 groups in order: 'a', 'e', 'i', 'o', 'u'
23    for (let i = 0; i < groups.length - 4; i++) {
24        // Extract the 5 consecutive groups
25        const [char1, count1] = groups[i];
26        const [char2, count2] = groups[i + 1];
27        const [char3, count3] = groups[i + 2];
28        const [char4, count4] = groups[i + 3];
29        const [char5, count5] = groups[i + 4];
30      
31        // Check if the sequence forms 'aeiou' in order
32        if (char1 === 'a' && char2 === 'e' && char3 === 'i' && 
33            char4 === 'o' && char5 === 'u') {
34            // Update maximum length with the total count of this beautiful substring
35            maxLength = Math.max(maxLength, count1 + count2 + count3 + count4 + count5);
36        }
37    }
38  
39    return maxLength;
40}
41

Time and Space Complexity

The time complexity is O(n), where n is the length of the string word.

The algorithm makes a single pass through the string to build the arr list. In the first while loop, each character is visited exactly once as the pointer i moves from 0 to n. Even though there's a nested while loop with pointer j, it doesn't increase the complexity because j starts where i is and moves forward, and then i jumps to where j ended. This ensures each character is processed only once.

The second loop iterates through the arr list, which in the worst case has at most n elements (when every character is different). The loop performs constant time operations for each window of size 5, making this part also O(n).

The space complexity is O(n). The arr list stores tuples containing characters and their counts. In the worst case, when all characters are different, the arr list will have n tuples. Each tuple takes constant space, so the total space used is proportional to the number of groups, which is at most n.

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

Common Pitfalls

Pitfall 1: Assuming Any 5 Groups Form a Valid Substring

The Problem: A common mistake is assuming that any 5 consecutive groups in the array automatically represent a contiguous substring in the original string. However, the grouping process only ensures that identical consecutive characters are grouped together - it doesn't validate that these groups form a continuous sequence containing all required vowels in order.

Why This Happens: Developers might overlook that the string could have patterns like "aeiouaeiou" which would create 10 groups: [('a',1), ('e',1), ('i',1), ('o',1), ('u',1), ('a',1), ('e',1), ('i',1), ('o',1), ('u',1)]. When checking groups at index 3, we'd examine [('o',1), ('u',1), ('a',1), ('e',1), ('i',1)], which doesn't form "aeiou" and isn't a valid beautiful substring.

The Solution: The current implementation correctly handles this by explicitly checking if the concatenated characters equal "aeiou". This ensures we only count substrings where the vowels appear in the exact required order.

Pitfall 2: Edge Case with Fewer Than 5 Groups

The Problem: If the input string creates fewer than 5 groups (e.g., "aaa" creates just 1 group, or "aaaeee" creates 2 groups), the loop condition range(len(grouped_chars) - 4) could cause issues if not handled properly.

Why This Happens: When len(grouped_chars) < 5, the expression len(grouped_chars) - 4 becomes negative or zero, making the range empty. While this doesn't cause an error (the loop simply doesn't execute), developers might not realize that this implicitly handles the case where no beautiful substring can exist.

The Solution: The current implementation correctly handles this edge case automatically. When there are fewer than 5 groups, the loop doesn't execute and returns 0, which is the correct answer since we need at least 5 distinct vowel segments to form a beautiful substring.

Pitfall 3: Misunderstanding the Grouping Logic

The Problem: Developers might incorrectly implement the grouping phase, especially the pointer movement logic. A common error is forgetting to move the outer pointer i to j after creating a group, causing infinite loops or incorrect groupings.

Incorrect Implementation Example:

while i < n:
    j = i
    while j < n and word[j] == word[i]:
        j += 1
    grouped_chars.append((word[i], j - i))
    i += 1  # WRONG: Should be i = j

Why This Happens: The increment i += 1 would cause overlapping counts and incorrect group boundaries. For "aaa", this would create three groups [('a', 3), ('a', 2), ('a', 1)] instead of one group [('a', 3)].

The Solution: Always set i = j after processing a group to ensure the next iteration starts at the first character of the next distinct vowel. This maintains non-overlapping, consecutive groups that accurately represent the original string structure.

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

Given a sorted array of integers and an integer called target, find the element that equals to the target and return its index. Select the correct code that fills the ___ in the given code snippet.

1def binary_search(arr, target):
2    left, right = 0, len(arr) - 1
3    while left ___ right:
4        mid = (left + right) // 2
5        if arr[mid] == target:
6            return mid
7        if arr[mid] < target:
8            ___ = mid + 1
9        else:
10            ___ = mid - 1
11    return -1
12
1public static int binarySearch(int[] arr, int target) {
2    int left = 0;
3    int right = arr.length - 1;
4
5    while (left ___ right) {
6        int mid = left + (right - left) / 2;
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16
1function binarySearch(arr, target) {
2    let left = 0;
3    let right = arr.length - 1;
4
5    while (left ___ right) {
6        let mid = left + Math.trunc((right - left) / 2);
7        if (arr[mid] == target) return mid;
8        if (arr[mid] < target) {
9            ___ = mid + 1;
10        } else {
11            ___ = mid - 1;
12        }
13    }
14    return -1;
15}
16

Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More