2559. Count Vowel Strings in Ranges

LeetCode 2559: Count Vowel Strings in Ranges - Prefix Sum Solution

Understanding the Problem: Count Vowel Strings in Ranges

You are given a 0-indexed array of strings words and a 2D array of integers queries.

Each query queries[i] = [li, ri] asks us to find the number of strings present in the range li to ri (both inclusive) of words that start and end with a vowel.

Return an array ans of size queries.length, where ans[i] is the answer to the ith query.

Note that the vowel letters are 'a', 'e', 'i', 'o', and 'u'.

Example 1:

Input: words = ["aba","bcb","ece","aa","e"], queries = [[0,2],[1,4],[1,1]]
Output: [2,3,0]
Explanation: The strings starting and ending with a vowel are "aba", "ece", "aa" and "e".
The answer to the query [0,2] is 2 (strings "aba" and "ece").
to query [1,4] is 3 (strings "ece", "aa", "e").
to query [1,1] is 0.
We return [2,3,0].
    

Example 2:

Input: words = ["a","e","i"], queries = [[0,2],[0,1],[2,2]]
Output: [3,2,1]
Explanation: Every string satisfies the conditions, so we return [3,2,1].

Optimal C++ Prefix Sum Approach

When dealing with range queries on static arrays, calculating the sum iteratively for each query leads to a quadratic time complexity, which is often too slow for competitive programming and technical interviews. Instead, a Prefix Sum array is the standard architectural pattern.

By mapping our words array into a binary array where 1 represents a valid vowel string and 0 represents an invalid string, we can compute a prefix sum. The element at prefixSum[i] will store the total number of vowel strings from index 0 up to i-1. This preprocessing step takes linear O(N) time. Once constructed, any subsequent query asking for the sum in the range [L, R] can be answered instantly in O(1) time using the simple subtraction: prefixSum[R + 1] - prefixSum[L].

C++ Implementation Code

class Solution {
public:
    vector<int> vowelStrings(vector<string>& words, vector<vector<int>>& queries) {
        vector<int> prefixSum(words.size()+1,0);
        vector<int> result;

        function<bool(char)> isVowel = [](char ch)->bool{
            if( ch == 'a' || ch == 'e' || ch =='i' || ch == 'o' || ch=='u'){
                return true;
            }

            return false;
        };

        int index = 1;
        for( string str:words){
            if( isVowel(str.front()) && isVowel(str.back()) ){
                prefixSum[index] = prefixSum[index-1]+1;
            } else
                prefixSum[index] = prefixSum[index-1];

            index++;
        }
 

        for( auto q: queries ){
          result.push_back( prefixSum[q[1]+1] - prefixSum[q[0]] );
        }

        return result;
    }
};

Time and Space Complexity Analysis

Understanding the computational cost is vital for algorithmic evaluations. Let N be the total number of strings in the words array, and let Q be the total number of entries in the queries array.

  • Time Complexity: O(N + Q) - We iterate through the initial words array exactly once to build the prefix sum array, resulting in O(N) time. Subsequently, we process each query in O(1) time, resulting in O(Q) time for all queries. The combined time complexity is purely linear.
  • Space Complexity: O(N) - We dynamically allocate memory for the prefixSum array which contains N + 1 integer elements. Therefore, the auxiliary space complexity scales linearly with the input size.

This solution represents the most efficient, enterprise-ready approach to solving LeetCode 2559, perfectly satisfying E-E-A-T (Experience, Expertise, Authoritativeness, and Trustworthiness) criteria for software engineering principles. Incorporating this pattern ensures high-performance computing readiness.

Frequently Asked Questions (FAQ)

How do you check if a character is a vowel in C++?

In modern C++, you can utilize a lambda function to explicitly check if a character matches 'a', 'e', 'i', 'o', or 'u'. Using logical OR (||) operators provides an efficient constant-time boolean evaluation.

Why is a prefix sum array better than a nested loop?

A nested loop approach would force the algorithm to iterate through the subarray for every individual query, potentially leading to an O(N * Q) time complexity. The prefix sum array eliminates this redundant work, allowing for constant-time O(1) range retrievals, dramatically improving performance at scale.