LeetCode 2374 Count Vowel Strings in Ranges 详解:基于 leetcode 多语言题解的前缀和与位掩码优化
LeetCode 2374 Count Vowel Strings in Ranges 详解基于 leetcode 多语言题解的前缀和与位掩码优化【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode本篇技术指南围绕 LeetCode 2374「统计范围内的元音字符串Count Vowel Strings in Ranges」展开完整继承 articles/count-vowel-strings-in-ranges.md 中的三套解法暴力枚举、前缀和 哈希集合、前缀和 位掩码及常见陷阱并结合 leetcode 仓库的多语言题解组织方式做补充说明。读完后你将掌握「区间计数」类问题的前缀和建模方法、位掩码做成员判断的技巧以及 10 种语言下的可复制实现代码。1. 问题背景与前置知识1.1 问题定义给定一个由小写英文字母组成的单词数组words长度为 n和 m 个查询queries每个查询为一个下标对[l, r]。若一个单词的首字符与尾字符都是元音字母a、e、i、o、u则称其为「元音字符串vowel string」。对每个查询需要统计words[l .. r]闭区间内元音字符串的个数并返回长度等于 m 的结果数组。判断条件有两个关键点必须同时满足首字符、尾字符为元音缺一不可单词本身长度可能为 1此时首尾是同一个字符需同时命中元音集合才算有效。1.2 前置知识Prerequisites原文档列出了三道前置技术点它们是理解后文三种解法的基石技术点作用对应解法Prefix Sum前缀和预计算累积计数将任意区间查询降到 O(1)解法二、三Hash Set哈希集合用集合实现 O(1) 的字符元音判定解法一、二Bit Manipulation位运算可选用位掩码代替哈希集合做成员判断解法三2. 解法一暴力枚举 Brute Force2.1 直觉最直接的思路是对每个查询(start, end)直接遍历该范围内的每个单词逐个检查首尾字符是否都是元音并累加计数。查询之间互不共享任何中间结果因此总工作量与「单词总数 × 查询数」同阶。2.2 算法步骤创建一个元音集合vowels {a,e,i,o,u}用于 O(1) 查询初始化空的结果列表对每个查询(start, end)初始化计数器cnt 0遍历下标start到end的每个单词检查首字符与尾字符是否都在元音集合中若是cnt 1将cnt追加到结果列表返回结果列表。2.3 参考实现10 种语言Pythonclass Solution: def vowelStrings(self, words: List[str], queries: List[List[int]]) - List[int]: vowels set(aeiou) res [] for start, end in queries: cnt 0 for i in range(start, end 1): if words[i][0] in vowels and words[i][-1] in vowels: cnt 1 res.append(cnt) return resJavapublic class Solution { public int[] vowelStrings(String[] words, int[][] queries) { SetCharacter vowels Set.of(a, e, i, o, u); int[] res new int[queries.length]; for (int k 0; k queries.length; k) { int start queries[k][0], end queries[k][1], count 0; for (int i start; i end; i) { String word words[i]; if (vowels.contains(word.charAt(0)) vowels.contains(word.charAt(word.length() - 1))) { count; } } res[k] count; } return res; } }Cclass Solution { public: vectorint vowelStrings(vectorstring words, vectorvectorint queries) { unordered_setchar vowels {a, e, i, o, u}; vectorint res; for (auto q : queries) { int start q[0], end q[1], count 0; for (int i start; i end; i) { if (vowels.count(words[i][0]) vowels.count(words[i].back())) { count; } } res.push_back(count); } return res; } };JavaScriptclass Solution { /** * param {string[]} words * param {number[][]} queries * return {number[]} */ vowelStrings(words, queries) { const vowels new Set([a, e, i, o, u]); const res []; for (let [start, end] of queries) { let count 0; for (let i start; i end; i) { const word words[i]; if (vowels.has(word[0]) vowels.has(word[word.length - 1])) { count; } } res.push(count); } return res; } }C#public class Solution { public int[] VowelStrings(string[] words, int[][] queries) { HashSetchar vowels new HashSetchar { a, e, i, o, u }; int[] res new int[queries.Length]; for (int k 0; k queries.Length; k) { int start queries[k][0], end queries[k][1], count 0; for (int i start; i end; i) { string word words[i]; if (vowels.Contains(word[0]) vowels.Contains(word[word.Length - 1])) { count; } } res[k] count; } return res; } }Gofunc vowelStrings(words []string, queries [][]int) []int { vowels : map[byte]bool{a: true, e: true, i: true, o: true, u: true} res : make([]int, len(queries)) for k, q : range queries { start, end, count : q[0], q[1], 0 for i : start; i end; i { word : words[i] if vowels[word[0]] vowels[word[len(word)-1]] { count } } res[k] count } return res }Kotlinclass Solution { fun vowelStrings(words: ArrayString, queries: ArrayIntArray): IntArray { val vowels setOf(a, e, i, o, u) val res IntArray(queries.size) for (k in queries.indices) { val (start, end) queries[k] var count 0 for (i in start..end) { val word words[i] if (word.first() in vowels word.last() in vowels) { count } } res[k] count } return res } }Swiftclass Solution { func vowelStrings(_ words: [String], _ queries: [[Int]]) - [Int] { let vowels: SetCharacter [a, e, i, o, u] var res [Int]() for q in queries { let start q[0], end q[1] var count 0 for i in start...end { let word words[i] if vowels.contains(word.first!) vowels.contains(word.last!) { count 1 } } res.append(count) } return res } }Rustimpl Solution { pub fn vowel_strings(words: VecString, queries: VecVeci32) - Veci32 { let vowels: HashSetu8 [ba, be, bi, bo, bu].into(); let mut res Vec::with_capacity(queries.len()); for q in queries { let (start, end) (q[0] as usize, q[1] as usize); let mut count 0; for i in start..end { let w words[i].as_bytes(); if vowels.contains(w[0]) vowels.contains(w[w.len() - 1]) { count 1; } } res.push(count); } res } }TypeScriptclass Solution { /** * param {string[]} words * param {number[][]} queries * return {number[]} */ vowelStrings(words: string[], queries: number[][]): number[] { const vowels new Set([a, e, i, o, u]); const res: number[] []; for (const [start, end] of queries) { let count 0; for (let i start; i end; i) { const word words[i]; if (vowels.has(word[0]) vowels.has(word[word.length - 1])) { count; } } res.push(count); } return res; } }2.4 复杂度时间复杂度O(n * m)n 为单词数量m 为查询数量最坏情况下每个查询都要遍历全部单词空间复杂度额外O(1)输出列表占O(m)。当 n 与 m 都接近 10^4 量级时O(n * m)可达 10^8 次字符检查已逼近超时风险因此需要引入前缀和。3. 解法二前缀和 哈希集合 Prefix Sum Hash Set3.1 直觉暴力法的核心问题是重复劳动多个查询之间存在大量重叠区间每次都重新扫描。由于「元音字符串判定」对每个单词只依赖其自身、且与查询无关可以一次性预计算前缀计数数组prefix[i]——表示从下标0到i-1共 i 个单词中元音字符串的个数。这样任意区间查询(l, r)只需一次减法即可 O(1) 回答answer(l, r) prefix[r 1] - prefix[l]这就是标准的「静态区间求和」前缀和模型与 LeetCode 303 Range Sum Query - Immutable 同型仓库中也有对应多语言题解例如 python/0303-range-sum-query-immutable.py。3.2 算法步骤创建元音集合用于 O(1) 查询建立长度为n 1的前缀数组prefixCnt初始全 0prefixCnt[0] 0作为哨兵方便闭区间查询对下标i处的单词prefixCnt[i 1] prefixCnt[i]若该单词首尾均为元音则prefixCnt[i 1] 1对每个查询(l, r)答案为prefixCnt[r 1] - prefixCnt[l]返回结果数组。注意前缀数组比words多一位n 1右端点必须取r 1这是后续「常见陷阱」中第一个错误点的根源。3.3 参考实现10 种语言Pythonclass Solution: def vowelStrings(self, words: List[str], queries: List[List[int]]) - List[int]: vowel_set set(aeiou) prefix_cnt [0] * (len(words) 1) prev 0 for i, w in enumerate(words): if w[0] in vowel_set and w[-1] in vowel_set: prev 1 prefix_cnt[i 1] prev res [0] * len(queries) for i, q in enumerate(queries): l, r q res[i] prefix_cnt[r 1] - prefix_cnt[l] return resJavapublic class Solution { public int[] vowelStrings(String[] words, int[][] queries) { SetCharacter vowels Set.of(a, e, i, o, u); int n words.length; int[] prefixCnt new int[n 1]; for (int i 0; i n; i) { String w words[i]; prefixCnt[i 1] prefixCnt[i]; if (vowels.contains(w.charAt(0)) vowels.contains(w.charAt(w.length() - 1))) { prefixCnt[i 1]; } } int[] res new int[queries.length]; for (int i 0; i queries.length; i) { int l queries[i][0], r queries[i][1]; res[i] prefixCnt[r 1] - prefixCnt[l]; } return res; } }Cclass Solution { public: vectorint vowelStrings(vectorstring words, vectorvectorint queries) { unordered_setchar vowels {a, e, i, o, u}; int n words.size(); vectorint prefixCnt(n 1, 0); for (int i 0; i n; i) { prefixCnt[i 1] prefixCnt[i]; if (vowels.count(words[i][0]) vowels.count(words[i].back())) { prefixCnt[i 1]; } } vectorint res; for (auto q : queries) { int l q[0], r q[1]; res.push_back(prefixCnt[r 1] - prefixCnt[l]); } return res; } };JavaScriptclass Solution { /** * param {string[]} words * param {number[][]} queries * return {number[]} */ vowelStrings(words, queries) { const vowels new Set([a, e, i, o, u]); const n words.length; const prefixCnt new Array(n 1).fill(0); for (let i 0; i n; i) { prefixCnt[i 1] prefixCnt[i]; const w words[i]; if (vowels.has(w[0]) vowels.has(w[w.length - 1])) { prefixCnt[i 1]; } } const res new Array(queries.length); for (let i 0; i queries.length; i) { const [l, r] queries[i]; res[i] prefixCnt[r 1] - prefixCnt[l]; } return res; } }C#public class Solution { public int[] VowelStrings(string[] words, int[][] queries) { HashSetchar vowels new HashSetchar { a, e, i, o, u }; int n words.Length; int[] prefixCnt new int[n 1]; for (int i 0; i n; i) { string w words[i]; prefixCnt[i 1] prefixCnt[i]; if (vowels.Contains(w[0]) vowels.Contains(w[w.Length - 1])) { prefixCnt[i 1]; } } int[] res new int[queries.Length]; for (int i 0; i queries.Length; i) { int l queries[i][0], r queries[i][1]; res[i] prefixCnt[r 1] - prefixCnt[l]; } return res; } }Gofunc vowelStrings(words []string, queries [][]int) []int { vowels : map[byte]bool{a: true, e: true, i: true, o: true, u: true} n : len(words) prefixCnt : make([]int, n1) for i : 0; i n; i { w : words[i] prefixCnt[i1] prefixCnt[i] if vowels[w[0]] vowels[w[len(w)-1]] { prefixCnt[i1] } } res : make([]int, len(queries)) for i, q : range queries { l, r : q[0], q[1] res[i] prefixCnt[r1] - prefixCnt[l] } return res }Kotlinclass Solution { fun vowelStrings(words: ArrayString, queries: ArrayIntArray): IntArray { val vowels setOf(a, e, i, o, u) val n words.size val prefixCnt IntArray(n 1) for (i in 0 until n) { val w words[i] prefixCnt[i 1] prefixCnt[i] if (w.first() in vowels w.last() in vowels) { prefixCnt[i 1] } } val res IntArray(queries.size) for (i in queries.indices) { val (l, r) queries[i] res[i] prefixCnt[r 1] - prefixCnt[l] } return res } }Swiftclass Solution { func vowelStrings(_ words: [String], _ queries: [[Int]]) - [Int] { let vowels: SetCharacter [a, e, i, o, u] let n words.count var prefixCnt Int for i in 0..n { let w words[i] prefixCnt[i 1] prefixCnt[i] if vowels.contains(w.first!) vowels.contains(w.last!) { prefixCnt[i 1] 1 } } var res [Int]() for q in queries { let l q[0], r q[1] res.append(prefixCnt[r 1] - prefixCnt[l]) } return res } }Rustimpl Solution { pub fn vowel_strings(words: VecString, queries: VecVeci32) - Veci32 { let vowels: HashSetu8 [ba, be, bi, bo, bu].into(); let n words.len(); let mut prefix_cnt vec![0i32; n 1]; for i in 0..n { let w words[i].as_bytes(); prefix_cnt[i 1] prefix_cnt[i]; if vowels.contains(w[0]) vowels.contains(w[w.len() - 1]) { prefix_cnt[i 1] 1; } } queries .iter() .map(|q| { let (l, r) (q[0] as usize, q[1] as usize); prefix_cnt[r 1] - prefix_cnt[l] }) .collect() } }TypeScriptclass Solution { /** * param {string[]} words * param {number[][]} queries * return {number[]} */ vowelStrings(words: string[], queries: number[][]): number[] { const vowels new Set([a, e, i, o, u]); const n words.length; const prefixCnt: number[] new Array(n 1).fill(0); for (let i 0; i n; i) { prefixCnt[i 1] prefixCnt[i]; const w words[i]; if (vowels.has(w[0]) vowels.has(w[w.length - 1])) { prefixCnt[i 1]; } } const res: number[] new Array(queries.length); for (let i 0; i queries.length; i) { const [l, r] queries[i]; res[i] prefixCnt[r 1] - prefixCnt[l]; } return res; } }3.4 复杂度时间复杂度O(n m)——一次线性扫描建前缀每个查询 O(1)空间复杂度额外O(n)前缀数组输出O(m)。3.5 实现要点从各语言实现可以看到几个值得注意的工程细节哨兵位prefixCnt[0] 0使左端点直接以l下标相减即可无需特殊处理l 0的情况Python 版用局部变量prev逐步累加后赋给prefix_cnt[i 1]避免反复读写数组下标语义与prefixCnt[i1] prefixCnt[i] isVowel等价Java/C 等采用「先拷贝、再增量」的两步写法prefix[i1] prefix[i]命中后与 Rust 版iter().map().collect()的函数式写法在逻辑上完全一致。4. 解法三前缀和 位掩码 Prefix Sum Bitmask4.1 直觉哈希集合虽然也是 O(1) 判元音但引入了堆分配与哈希开销。由于元音只有 5 个、小写字母只有 26 个可以用一个整数的 26 个二进制位编码「哪些字符是元音」位i为 1 当且仅当字符a i是元音。判断某字符c是否元音退化为一次按位与isVowel(c) ((1 (c - a)) vowels) ! 0原文档给出的掩码构造方式为循环左移取并for c in aeiou: vowels | 1 (ord(c) - ord(a))对应各元音占用的位a→bit 0、e→bit 4、i→bit 8、o→bit 14、u→bit 20。可推断该掩码是一个常量2^0 2^4 2^8 2^14 2^20 1 16 256 16384 1048576 1065233因此熟练后甚至可以直接写const VOYEL_MASK 1065233建议保留循环构造法可读性更好且不易写错。4.2 算法步骤通过循环置位构造元音位掩码a、e、i、o、u建立长度为n 1的前缀数组prefix对每个单词检查首字符对应位是否在掩码中置位检查尾字符对应位是否在掩码中置位两者均满足则前缀计数加 1对每个查询(l, r)答案为prefix[r 1] - prefix[l]返回结果数组。4.3 参考实现10 种语言Pythonclass Solution: def vowelStrings(self, words: List[str], queries: List[List[int]]) - List[int]: vowels sum(1 (ord(c) - ord(a)) for c in aeiou) prefix [0] for w in words: prefix.append(prefix[-1]) if (1 (ord(w[0]) - ord(a))) vowels and (1 (ord(w[-1]) - ord(a))) vowels: prefix[-1] 1 return [prefix[r 1] - prefix[l] for l, r in queries]Javapublic class Solution { public int[] vowelStrings(String[] words, int[][] queries) { int vowels 0; for (char c : aeiou.toCharArray()) { vowels | 1 (c - a); } int[] prefix new int[words.length 1]; for (int i 0; i words.length; i) { int f words[i].charAt(0) - a; int l words[i].charAt(words[i].length() - 1) - a; int isVowel ((1 f) vowels) ! 0 ((1 l) vowels) ! 0 ? 1 : 0; prefix[i 1] prefix[i] isVowel; } int[] res new int[queries.length]; for (int i 0; i queries.length; i) { int l queries[i][0], r queries[i][1]; res[i] prefix[r 1] - prefix[l]; } return res; } }Cclass Solution { public: vectorint vowelStrings(vectorstring words, vectorvectorint queries) { int vowels 0; for (char c : string(aeiou)) { vowels | (1 (c - a)); } int n words.size(); vectorint prefix(n 1); for (int i 0; i n; i) { int f words[i][0] - a; int l words[i].back() - a; int isVowel ((1 f) vowels) ((1 l) vowels); prefix[i 1] prefix[i] isVowel; } vectorint res; for (auto q : queries) { int l q[0], r q[1]; res.push_back(prefix[r 1] - prefix[l]); } return res; } };JavaScriptclass Solution { /** * param {string[]} words * param {number[][]} queries * return {number[]} */ vowelStrings(words, queries) { let vowels 0; for (let c of aeiou) { vowels | 1 (c.charCodeAt(0) - 97); } const prefix [0]; for (let w of words) { const f w.charCodeAt(0) - 97; const l w.charCodeAt(w.length - 1) - 97; const isVowel (1 f) vowels (1 l) vowels ? 1 : 0; prefix.push(prefix[prefix.length - 1] isVowel); } return queries.map(([l, r]) prefix[r 1] - prefix[l]); } }C#public class Solution { public int[] VowelStrings(string[] words, int[][] queries) { int vowels 0; foreach (char c in aeiou) { vowels | 1 (c - a); } int[] prefix new int[words.Length 1]; for (int i 0; i words.Length; i) { int f words[i][0] - a; int l words[i][words[i].Length - 1] - a; int isVowel ((1 f) vowels) ! 0 ((1 l) vowels) ! 0 ? 1 : 0; prefix[i 1] prefix[i] isVowel; } int[] res new int[queries.Length]; for (int i 0; i queries.Length; i) { int l queries[i][0], r queries[i][1]; res[i] prefix[r 1] - prefix[l]; } return res; } }Gofunc vowelStrings(words []string, queries [][]int) []int { vowels : 0 for _, c : range aeiou { vowels | 1 (c - a) } n : len(words) prefix : make([]int, n1) for i : 0; i n; i { f : int(words[i][0] - a) l : int(words[i][len(words[i])-1] - a) isVowel : 0 if (1f)vowels ! 0 (1l)vowels ! 0 { isVowel 1 } prefix[i1] prefix[i] isVowel } res : make([]int, len(queries)) for i, q : range queries { l, r : q[0], q[1] res[i] prefix[r1] - prefix[l] } return res }Kotlinclass Solution { fun vowelStrings(words: ArrayString, queries: ArrayIntArray): IntArray { var vowels 0 for (c in aeiou) { vowels vowels or (1 shl (c - a)) } val prefix IntArray(words.size 1) for (i in words.indices) { val f words[i][0] - a val l words[i].last() - a val isVowel if ((1 shl f) and vowels ! 0 (1 shl l) and vowels ! 0) 1 else 0 prefix[i 1] prefix[i] isVowel } return IntArray(queries.size) { i - val (lo, hi) queries[i] prefix[hi 1] - prefix[lo] } } }Swiftclass Solution { func vowelStrings(_ words: [String], _ queries: [[Int]]) - [Int] { var vowels 0 for c in aeiou { vowels | 1 (Int(c.asciiValue!) - 97) } var prefix [0] for w in words { let f Int(w.first!.asciiValue!) - 97 let l Int(w.last!.asciiValue!) - 97 let isVowel ((1 f) vowels ! 0 (1 l) vowels ! 0) ? 1 : 0 prefix.append(prefix.last! isVowel) } return queries.map { q in let l q[0], r q[1] return prefix[r 1] - prefix[l] } } }Rustimpl Solution { pub fn vowel_strings(words: VecString, queries: VecVeci32) - Veci32 { let mut vowels 0u32; for c in aeiou.bytes() { vowels | 1 (c - ba); } let n words.len(); let mut prefix vec![0i32; n 1]; for i in 0..n { let w words[i].as_bytes(); let f w[0] - ba; let l w[w.len() - 1] - ba; let is_vowel if (1 f) vowels ! 0 (1 l) vowels ! 0 { 1 } else { 0 }; prefix[i 1] prefix[i] is_vowel; } queries .iter() .map(|q| { let (l, r) (q[0] as usize, q[1] as usize); prefix[r 1] - prefix[l] }) .collect() } }TypeScriptclass Solution { /** * param {string[]} words * param {number[][]} queries * return {number[]} */ vowelStrings(words: string[], queries: number[][]): number[] { let vowels 0; for (const c of aeiou) { vowels | 1 (c.charCodeAt(0) - 97); } const prefix: number[] [0]; for (const w of words) { const f w.charCodeAt(0) - 97; const l w.charCodeAt(w.length - 1) - 97; const isVowel (1 f) vowels (1 l) vowels ? 1 : 0; prefix.push(prefix[prefix.length - 1] isVowel); } return queries.map(([l, r]) prefix[r 1] - prefix[l]); } }4.4 复杂度时间复杂度O(n m)空间复杂度额外O(n)输出O(m)。4.5 语言层面的位运算细节从 10 份实现中可以归纳出移植位掩码方案时的几个关键点字符到位移量的转换C/Java/C 直接c - aJS/TS 用charCodeAt(0) - 97Swift 用asciiValue! - 97Rust 用as_bytes()后c - baKotlin 用1 shl x表示左移。本质都是把字符映射到0..25移位量上限小写字母最大位移 25z - a任何 32 位整型都足够容纳无溢出风险按位与的返回值差异C 中((1 f) vowels)直接得到 0/非 0 整数可参与布尔表达式如int isVowel ((1 f) vowels) ((1 l) vowels);Java、C# 显式比较! 0Go、Rust 也显式写! 0。跨语言对照阅读时注意这一点避免漏写比较。5. 常见陷阱 Common Pitfalls原文档总结了三个高频错误配合反例代码说明5.1 前缀和查询的 Off-by-One最经典的错误是把查询写成prefix[r] - prefix[l]漏掉了右端点的1。前缀数组是「以 1 为基的偏移式」prefix[i]表示[0, i-1]的累计因此闭区间[l, r]的右端必须取r 1# Incorrect - excludes the element at index r res[i] prefix[r] - prefix[l] # Correct - includes the element at index r res[i] prefix[r 1] - prefix[l]5.2 只检查首字符或尾字符元音字符串必须首尾同时为元音。只写一个条件会把大量单词误判进来# Incorrect - only checks starting character if words[i][0] in vowels: cnt 1 # Correct - checks both first and last characters if words[i][0] in vowels and words[i][-1] in vowels: cnt 15.3 元音集合/掩码漏项构造元音集合或掩码时漏掉 5 个元音之一a、e、i、o、u会导致系统性漏计手工拼掩码常量时尤其容易出错# Incorrect - missing u vowels set(aeio) # Correct - all five vowels included vowels set(aeiou)这也是前文建议保留「循环置位构造掩码」而非硬编码1065233的原因——构造过程本身就是一种自检。6. 三种解法对比与仓库组织说明6.1 复杂度对比解法时间复杂度额外空间适用场景暴力枚举O(n * m)O(1)教学理解、n·m 很小的场景前缀和 哈希集合O(n m)O(n)通用最优可读性最好前缀和 位掩码O(n m)O(n)追求常数性能、展示位技巧其中 n 为单词数量m 为查询数量。三种解法共享同一个正确性骨架首尾双元音判定 区间闭端语义差异仅在「成员判定数据结构」无 / 哈希集合 / 位掩码与「区间统计方式」逐次扫描 / 前缀减法。6.2 在 leetcode 仓库中的位置本文所有算法、代码与陷阱章节均完整继承自 articles/count-vowel-strings-in-ranges.md原文档中每个解法以 Python、Java、C、JavaScript、C#、Go、Kotlin、Swift、Rust、TypeScript 共 10 种语言并列给出仓库的题解文章规范见 articles/README.md每篇文章要求覆盖尽可能多的解法且至少一种与 NeetCode 视频解法一致、标注时间与空间复杂度——原文档的结构正是按该规范编写的这也解释了为何每种语言实现都重复完整呈现这 10 种语言与 README.md 中列出的 NeetCode 支持语言清单Python、Java、JavaScript、C、Go、Swift、C#、TypeScript、Rust、Kotlin 等相对应仓库按python/、java/、cpp/等语言目录存放其他题目的解码文件文件命名遵循「题号-题目短横线命名」惯例例如 python/1456-maximum-number-of-vowels-in-a-substring-of-given-length.py。需要注意的是本仓库当前各语言目录中并未收录 2374 号题的独立代码文件因此本文的 30 段实现代码全部以原文档为唯一事实来源可直接复制到 LeetCode 判题环境运行。6.3 小结面对「静态数组 大量区间计数/求和查询」第一反应应是前缀和一次性 O(n) 预处理换取 O(1) 查询字符类成员判定可用哈希集合通用或位掩码26 个小写字母场景下的零分配优化移植前务必守住三条正确性底线右端点1、首尾双条件、元音五要素一个不能少。【免费下载链接】leetcodeLeetcode solutions项目地址: https://gitcode.com/GitHub_Trending/leetcode1/leetcode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考