2586. Count the Number of Vowel Strings in Range
Problem Description
You are given an array of strings words
(0-indexed) and two integers left
and right
.
A vowel string is defined as a string that:
- Starts with a vowel character ('a', 'e', 'i', 'o', or 'u')
- Ends with a vowel character ('a', 'e', 'i', 'o', or 'u')
Your task is to count how many strings in the subarray words[left]
to words[right]
(inclusive) are vowel strings.
For example:
- "apple" is a vowel string (starts with 'a', ends with 'e')
- "orange" is a vowel string (starts with 'o', ends with 'e')
- "bat" is NOT a vowel string (starts with 'b', which is not a vowel)
- "end" is NOT a vowel string (ends with 'd', which is not a vowel)
The solution iterates through each word in the range [left, right]
and checks if both the first character w[0]
and last character w[-1]
are vowels. It uses Python's in
operator to check if a character exists in the string 'aeiou'
. The sum()
function counts how many words satisfy both conditions.
Intuition
The problem asks us to count strings that meet a specific criteria within a given range. The straightforward approach is to examine each string in the specified range and check if it satisfies our conditions.
For a string to be a "vowel string", we need to verify two conditions:
- The first character must be a vowel
- The last character must be a vowel
Since we only care about strings within the range [left, right]
, we can slice the array to get just the elements we need: words[left : right + 1]
.
For each word in this range, we can access the first character using w[0]
and the last character using w[-1]
(Python's negative indexing makes this convenient). To check if a character is a vowel, we can use the membership test in 'aeiou'
, which is both concise and efficient for small sets.
The key insight is that we can combine these checks into a single boolean expression: w[0] in 'aeiou' and w[-1] in 'aeiou'
. This expression evaluates to True
(which has a numeric value of 1) when both conditions are met, and False
(value of 0) otherwise.
By using Python's sum()
function with a generator expression, we can count all the True
values in one pass. This eliminates the need for explicit loops and counter variables, making the solution both elegant and efficient.
Solution Approach
The solution uses a direct simulation approach to solve the problem. We traverse through the strings in the specified range and count those that meet the vowel string criteria.
Implementation Steps:
-
Array Slicing: Extract the relevant portion of the array using Python's slice notation
words[left : right + 1]
. This gives us only the strings we need to examine, from indexleft
to indexright
inclusive. -
Vowel Checking: For each word
w
in the sliced array:- Check if the first character
w[0]
is in the set of vowels'aeiou'
- Check if the last character
w[-1]
is in the set of vowels'aeiou'
- Both conditions must be true for the word to be counted
- Check if the first character
-
Counting with Generator Expression: The solution uses a generator expression inside the
sum()
function:sum(w[0] in 'aeiou' and w[-1] in 'aeiou' for w in words[left : right + 1])
This expression generates a sequence of boolean values (
True
orFalse
) for each word. Whensum()
processes these values,True
is treated as 1 andFalse
as 0, effectively counting the number of vowel strings.
Time Complexity: O(n)
where n = right - left + 1
(the number of strings in the range). We visit each string in the range exactly once.
Space Complexity: O(1)
as we only use a constant amount of extra space. The generator expression doesn't create an intermediate list but evaluates one element at a time.
The beauty of this solution lies in its simplicity - it directly implements what the problem asks for without any unnecessary complexity or additional data structures.
Ready to land your dream job?
Unlock your dream job with a 3-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to understand how the solution works.
Input:
words = ["apple", "bat", "orange", "end", "umbrella"]
left = 1
right = 4
Step 1: Extract the subarray
We need to examine words[1:5]
(indices 1 through 4 inclusive):
- Subarray:
["bat", "orange", "end", "umbrella"]
Step 2: Check each word
For "bat"
(index 1):
- First character:
'b'
→ Is it in'aeiou'
? No - Last character:
't'
→ Is it in'aeiou'
? No - Both conditions met? No → Count: 0
For "orange"
(index 2):
- First character:
'o'
→ Is it in'aeiou'
? Yes - Last character:
'e'
→ Is it in'aeiou'
? Yes - Both conditions met? Yes → Count: 1
For "end"
(index 3):
- First character:
'e'
→ Is it in'aeiou'
? Yes - Last character:
'd'
→ Is it in'aeiou'
? No - Both conditions met? No → Count: 0
For "umbrella"
(index 4):
- First character:
'u'
→ Is it in'aeiou'
? Yes - Last character:
'a'
→ Is it in'aeiou'
? Yes - Both conditions met? Yes → Count: 1
Step 3: Sum the results
The generator expression produces: False, True, False, True
When summed: 0 + 1 + 0 + 1 = 2
Result: The function returns 2
, indicating there are 2 vowel strings in the range.
Solution Implementation
1class Solution:
2 def vowelStrings(self, words: List[str], left: int, right: int) -> int:
3 """
4 Count the number of strings that start and end with a vowel
5 within the specified range [left, right] inclusive.
6
7 Args:
8 words: List of strings to check
9 left: Starting index of the range (inclusive)
10 right: Ending index of the range (inclusive)
11
12 Returns:
13 Number of strings that both start and end with a vowel
14 """
15 # Define the set of vowels for checking
16 vowels = 'aeiou'
17
18 # Initialize counter for valid strings
19 count = 0
20
21 # Iterate through the specified range of words
22 for i in range(left, right + 1):
23 word = words[i]
24
25 # Check if both first and last characters are vowels
26 if word[0] in vowels and word[-1] in vowels:
27 count += 1
28
29 return count
30
1class Solution {
2 /**
3 * Counts the number of strings that start and end with a vowel
4 * within the specified range [left, right] inclusive.
5 *
6 * @param words Array of strings to check
7 * @param left Starting index of the range (inclusive)
8 * @param right Ending index of the range (inclusive)
9 * @return Count of strings that start and end with vowels
10 */
11 public int vowelStrings(String[] words, int left, int right) {
12 int count = 0;
13
14 // Iterate through the specified range of words
15 for (int i = left; i <= right; i++) {
16 String currentWord = words[i];
17
18 // Check if both first and last characters are vowels
19 char firstChar = currentWord.charAt(0);
20 char lastChar = currentWord.charAt(currentWord.length() - 1);
21
22 if (isVowel(firstChar) && isVowel(lastChar)) {
23 count++;
24 }
25 }
26
27 return count;
28 }
29
30 /**
31 * Checks if a character is a vowel (a, e, i, o, u).
32 *
33 * @param c Character to check
34 * @return true if the character is a vowel, false otherwise
35 */
36 private boolean isVowel(char c) {
37 return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
38 }
39}
40
1class Solution {
2public:
3 int vowelStrings(vector<string>& words, int left, int right) {
4 // Lambda function to check if a character is a vowel
5 auto isVowel = [](char c) -> bool {
6 return c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u';
7 };
8
9 // Counter for strings that start and end with vowels
10 int count = 0;
11
12 // Iterate through the specified range [left, right]
13 for (int i = left; i <= right; ++i) {
14 const string& word = words[i];
15
16 // Check if both first and last characters are vowels
17 // If true, increment count by 1 (implicit bool to int conversion)
18 if (isVowel(word[0]) && isVowel(word[word.size() - 1])) {
19 count++;
20 }
21 }
22
23 return count;
24 }
25};
26
1/**
2 * Counts the number of strings that start and end with a vowel within the given range
3 * @param words - Array of strings to check
4 * @param left - Starting index of the range (inclusive)
5 * @param right - Ending index of the range (inclusive)
6 * @returns Number of strings that start and end with a vowel
7 */
8function vowelStrings(words: string[], left: number, right: number): number {
9 // Initialize counter for valid strings
10 let count: number = 0;
11
12 // Define the set of vowels to check against
13 const vowels: string[] = ['a', 'e', 'i', 'o', 'u'];
14
15 // Iterate through the specified range of words
16 for (let index: number = left; index <= right; index++) {
17 const currentWord: string = words[index];
18
19 // Check if both first and last characters are vowels
20 const firstChar: string = currentWord[0];
21 const lastChar: string | undefined = currentWord.at(-1);
22
23 if (vowels.includes(firstChar) && lastChar && vowels.includes(lastChar)) {
24 count++;
25 }
26 }
27
28 return count;
29}
30
Time and Space Complexity
Time Complexity: O(m)
where m = right - left + 1
The algorithm iterates through a slice of the words
list from index left
to right
(inclusive), which contains m = right - left + 1
elements. For each word in this range, it performs constant-time operations:
- Accessing the first character
w[0]
-O(1)
- Accessing the last character
w[-1]
-O(1)
- Checking membership in the string
'aeiou'
-O(1)
(since the string has constant size)
Therefore, the overall time complexity is O(m)
.
Space Complexity: O(1)
The algorithm uses only a constant amount of extra space:
- The generator expression doesn't create an intermediate list; it evaluates lazily
- The
sum()
function accumulates the result using a single variable - No additional data structures are created that depend on the input size
The slicing operation words[left : right + 1]
creates a view/reference rather than copying the elements, so it doesn't contribute to space complexity. Therefore, the space complexity is O(1)
.
Learn more about how to find time and space complexity quickly.
Common Pitfalls
1. Empty String Handling
One of the most common pitfalls is not considering the case where a word in the array might be an empty string. Accessing word[0]
or word[-1]
on an empty string will raise an IndexError
.
Problem Example:
words = ["apple", "", "orange"] left, right = 0, 2 # This will crash when processing the empty string at index 1
Solution: Add a check for empty strings before accessing the first and last characters:
class Solution:
def vowelStrings(self, words: List[str], left: int, right: int) -> int:
vowels = 'aeiou'
count = 0
for i in range(left, right + 1):
word = words[i]
# Check if word is not empty before accessing characters
if word and word[0] in vowels and word[-1] in vowels:
count += 1
return count
2. Case Sensitivity
Another pitfall is assuming all input strings are lowercase. If the input contains uppercase vowels, they won't be counted since 'aeiou'
only contains lowercase characters.
Problem Example:
words = ["Apple", "Orange", "ECHO"] # These won't be counted as vowel strings even though they should be
Solution: Convert characters to lowercase before checking:
class Solution:
def vowelStrings(self, words: List[str], left: int, right: int) -> int:
vowels = 'aeiou'
count = 0
for i in range(left, right + 1):
word = words[i]
if word and word[0].lower() in vowels and word[-1].lower() in vowels:
count += 1
return count
3. Single Character String Edge Case
While not necessarily a bug, it's worth noting that for single-character strings, word[0]
and word[-1]
refer to the same character. This means a single vowel character like "a" or "e" would be counted as a vowel string, which is correct but might not be immediately obvious.
Example:
words = ["a", "b", "e"] # "a" and "e" are both vowel strings (start and end with the same vowel)
This behavior is usually intended, but it's important to be aware of it when testing edge cases.
What is an advantages of top-down dynamic programming vs bottom-up dynamic programming?
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 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
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 represents the amount of time
Want a Structured Path to Master System Design Too? Don’t Miss This!