Opening the reading…
Opening the reading…
PREFIX SUM › PREFIX SUM
Open — the attempt gate is not wired up yet
This editorial is meant to unlock after you have run the problem at least once, with the worked solution behind one further deliberate click. That needs per-learner unlock state nothing stores today, so for now the whole article is open.
Try it yourself first →Each word contributes only one bit of information to every query: it either starts and ends with vowels, or it does not. First classify words[i] as 1 when both boundary characters are vowels and as 0 otherwise. The interior characters do not affect the answer, so examining them would add work without adding information.
Once the array contains only zeros and ones, store cumulative totals in a prefix array. Let prefix[i] count qualifying words before index i. Then the inclusive range l to r is exactly the total before r + 1 minus the total before l. The extra slot at prefix[0] makes this formula work unchanged when l is zero.
| MEASURE | BOUND | WHY |
|---|---|---|
| Time | O(n + q) | Each word contributes one prefix entry after two constant-time endpoint checks, and each query performs two array lookups and one subtraction. No word or query is revisited, so the two passes add rather than multiply their work. |
| Space | O(n) extra | The prefix array stores n + 1 cumulative values. The returned answer array is required output and is excluded from the extra-space bound. There is no input-shape degradation: the extra working space remains O(n) whether all words qualify, none qualify, or the qualifying words are clustered. |
#include <string>
#include <unordered_set>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> vowelStrings(vector<string>& words, vector<vector<int>>& queries) {
int n = static_cast<int>(words.size());
vector<int> prefix(n + 1, 0);
unordered_set<char> vowels = {'a', 'e', 'i', 'o', 'u'};
for (int i = 0; i < n; ++i) {
string& word = words[i];
bool qualifies = vowels.count(word[0]) && vowels.count(word.back());
prefix[i + 1] = prefix[i] + (qualifies ? 1 : 0);
}
vector<int> answer;
answer.reserve(queries.size());
for (const vector<int>& query : queries) {
int left = query[0];
int right = query[1];
answer.push_back(prefix[right + 1] - prefix[left]);
}
return answer;
}
};The indexing convention carries the main insight. prefix[i] stops before words[i], so prefix[left] removes exactly the elements before the query. The right endpoint is inclusive, which is why the kept total ends at prefix[right + 1]. The classification uses word[0] and word.back() only; checking the complete word would solve a different problem.