leetcode
leetcode 2251 ~ 2300
统计范围内的元音字符串数

统计范围内的元音字符串数

难度:

标签:

题目描述

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].

 

Constraints:

  • 1 <= words.length <= 105
  • 1 <= words[i].length <= 40
  • words[i] consists only of lowercase English letters.
  • sum(words[i].length) <= 3 * 105
  • 1 <= queries.length <= 105
  • 0 <= li <= ri < words.length

代码结果

运行时间: 63 ms, 内存: 46.7 MB


/*
 * Problem: Similar to the previous solution, but using Java Streams for a more functional approach.
 * Approach:
 * 1. Define a set of vowels.
 * 2. Use streams to calculate the prefix sum array.
 * 3. For each query, calculate the result using prefix sum array.
 */
import java.util.HashSet;
import java.util.Set;
import java.util.stream.IntStream;

public class VowelWordsStream {
    public int[] vowelStrings(String[] words, int[][] queries) {
        Set<Character> vowels = Set.of('a', 'e', 'i', 'o', 'u');
        int[] prefix = IntStream.range(0, words.length)
                                 .map(i -> isVowelWord(words[i], vowels) ? 1 : 0)
                                 .toArray();
        int[] cumulativePrefix = new int[words.length + 1];
        IntStream.range(0, prefix.length)
                 .forEach(i -> cumulativePrefix[i + 1] = cumulativePrefix[i] + prefix[i]);
        return IntStream.range(0, queries.length)
                        .map(i -> cumulativePrefix[queries[i][1] + 1] - cumulativePrefix[queries[i][0]])
                        .toArray();
    }

    private boolean isVowelWord(String word, Set<Character> vowels) {
        return vowels.contains(word.charAt(0)) && vowels.contains(word.charAt(word.length() - 1));
    }
}

解释

方法:

此题解使用了前缀和的方法来解决问题。首先,遍历 words 数组并构建一个前缀和数组 pre。在这个数组中,每个元素 pre[i] 表示从 words[0] 到 words[i-1] 的范围内,以元音开头和结尾的字符串的数量。如果当前字符串 word 以元音开头和结尾,则将前一个元素的前缀和加一;否则,保持前缀和不变。构建完前缀和数组后,对于每个查询 [l, r],可以通过计算 pre[r+1] - pre[l] 来得到下标范围 l 到 r 内以元音开头和结尾的字符串的数量。这样可以快速回答查询,避免重复计算。

时间复杂度:

O(n + q)

空间复杂度:

O(n + q)

代码细节讲解

🦆
为何在构建前缀和数组时,要初始化`pre`数组为`[0]`,而非空数组或其他初始值?
在构建前缀和数组时,初始化`pre`数组为`[0]`是为了方便计算从任何位置开始至任何位置结束的区间和。这个初始化为0的元素代表的是在任何字符串加入前的初始状态,即没有任何字符串时的前缀和。如果不初始化为`[0]`而是开始为空数组,那么在计算某些查询如`[0, r]`时,将无法直接使用`pre[r+1]`,因为`pre[0]`这个必要的基准点不存在。此外,初始化为0也避免了在数组非空但未包含任何符合条件的字符串时的错误累加。
🦆
在题解中提到的前缀和数组`pre[i]`表示的是`从words[0]到words[i-1]`的范围内以元音开头和结尾的字符串数量,那么为什么查询时使用`pre[r+1] - pre[l]`而不是`pre[r] - pre[l-1]`来得到答案?
这是因为`pre[i]`实际上代表的是从`words[0]`到`words[i-1]`(闭区间)的计数,所以`pre[r+1]`实际上是代表从`words[0]`到`words[r]`的计数。为了得到从`words[l]`到`words[r]`的计数,我们需要使用`pre[r+1]`减去`pre[l]`(代表从`words[0]`到`words[l-1]`的计数),这样就准确地得到了`l`到`r`区间的计数。如果使用`pre[r] - pre[l-1]`,则会漏掉`words[r]`这个位置的计数(如果它符合条件)。
🦆
代码中判断字符串是否以元音开头和结尾的条件是`word[0] in vowels and word[-1] in vowels`,这种方式是否考虑了字符串长度为1的情况?
是的,这种判断方式考虑了字符串长度为1的情况。当字符串长度为1时,`word[0]`和`word[-1]`都指向这个唯一的字符。因此,如果这个字符是元音,那么这个长度为1的字符串同时满足以元音开头和结尾的条件。
🦆
如果`words`数组为空,或者`queries`数组为空,该解决方案是否还会正确运行?需要做哪些额外的边界检查?
如果`words`数组为空,则`pre`数组仅包含初始的0,不会有任何增加的元素。在这种情况下,任何查询都应返回0,因为没有字符串符合条件。代码应当能够处理这种情况,因为`pre[r+1] - pre[l]`在r和l有效时返回0。如果`queries`数组为空,则最终返回的答案数组也应为空,没有任何额外的计算。确保解决方案正确处理这些情况,主要的边界检查应包括确保在访问`pre`数组元素前,其索引是有效的,避免越界错误。

相关问题