)
科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载本篇技术指南围绕 codeforces-go 仓库中 双周赛 109 第 B 题题解文档 展开系统拆解 LeetCode「Sort Vowels in a String排序字符串中的元音」一题的三种解法收集排序填空、位掩码0x208222优化、以及线性复杂度的计数排序并对照仓库内的 Go 实现、测试文件 与 测试数据 验证每行代码的真实出处。读完本文你既能掌握这道题从O(n log n)到O(n)的完整优化路径也能学会「集合论到位运算」这一可迁移的元音/字母集合判定技巧。一、问题回顾规则与关键例子的手推过程题目要求给定字符串s将其中的**元音字母a/e/i/o/u大小写均可**单独提取出来按ASCII 升序排序再按原顺序填回原字符串中元音所在的空位非元音字符位置保持不变。原文档给出了示例 1 的完整推演s lEetcOde其中元音字母为E、e、O、e排序后为EOee大写字母排前因为大写字母的 ASCII 值更小。把原串中的元音位置视作空位得到l__tc_d_共 4 个空位依次填入EOee后得到答案lEOtcede。仓库中的 测试数据 恰好收录了两个用例可直接对照验证lEetcOde lEOtcede lYmpH lYmpH第二个用例lYmpH中不含任何元音字母因此输出与原串完全一致——这是对「非元音位置不动」规则的最简回归用例。二、写法一提取 → 排序 → 填空O(n log n)思路最直观第一遍扫描把元音收集到数组中排序数组第二遍扫描遇到元音就按序「填空」。原文档给出了 Python3 / Java / C / C / Go / JavaScript / Rust 共 7 种语言的完整实现核心逻辑完全同构这里完整列出class Solution: def sortVowels(self, s: str) - str: vowels sorted(ch for ch in s if ch in AEIOUaeiou) t list(s) # str 无法修改转成 list j 0 for i, ch in enumerate(t): if ch in AEIOUaeiou: t[i] vowels[j] # 填空 j 1 return .join(t)class Solution { public String sortVowels(String S) { StringBuilder vowels new StringBuilder(); char[] s S.toCharArray(); for (char ch : s) { char c Character.toLowerCase(ch); if (c a || c e || c i || c o || c u) { vowels.append(ch); } } char[] sortedVowels vowels.toString().toCharArray(); Arrays.sort(sortedVowels); int j 0; for (int i 0; i s.length; i) { char c Character.toLowerCase(s[i]); if (c a || c e || c i || c o || c u) { s[i] sortedVowels[j]; } } return new String(s); } }class Solution { public: string sortVowels(string s) { string vowels; for (char ch : s) { char c tolower(ch); if (c a || c e || c i || c o || c u) { vowels ch; } } ranges::sort(vowels); int j 0; for (char ch : s) { char c tolower(ch); if (c a || c e || c i || c o || c u) { ch vowels[j]; } } return s; } };#define VOWEL_MASK 0x208222 int cmp(const void* a, const void* b) { return *(char*)a - *(char*)b; } char* sortVowels(char* s) { int n strlen(s); char* vowels malloc(n * sizeof(char)); int k 0; for (int i 0; i n; i) { char c tolower(s[i]); if (c a || c e || c i || c o || c u) { vowels[k] s[i]; } } qsort(vowels, k, sizeof(char), cmp); k 0; for (int i 0; i n; i) { char c tolower(s[i]); if (c a || c e || c i || c o || c u) { s[i] vowels[k]; } } free(vowels); return s; }func sortVowels(s string) string { vowels : []byte{} for _, ch : range s { c : unicode.ToLower(ch) if strings.ContainsRune(aeiou, c) { vowels append(vowels, byte(ch)) } } slices.Sort(vowels) t : []byte(s) j : 0 for i, ch : range t { c : unicode.ToLower(rune(ch)) if strings.ContainsRune(aeiou, c) { t[i] vowels[j] j } } return string(t) }var sortVowels function(s) { const vowels []; for (const ch of s) { if (AEIOUaeiou.includes(ch)) { vowels.push(ch); } } vowels.sort(); let j 0; const t s.split(); for (let i 0; i t.length; i) { if (AEIOUaeiou.includes(t[i])) { t[i] vowels[j]; } } return t.join(); };impl Solution { pub fn sort_vowels(s: String) - String { let mut vowels s.bytes() .filter(|ch| AEIOUaeiou.contains(ch as char)) .collect::Vec_(); vowels.sort_unstable(); let mut s s.into_bytes(); let mut j 0; for ch in s.iter_mut() { if AEIOUaeiou.contains(*ch as char) { *ch vowels[j]; j 1; } } unsafe { String::from_utf8_unchecked(s) } } }注意各语言的一个共同细节第二遍填空必须只修改元音位置且填入的是已排序元音数组中依次推进的下一个元素——指针j单调递增天然保证「小的元音先被填走」。三、写法二位掩码 0x208222 优化核心技巧3.1 原理ch 31统一大小写规则原文档给出了一条重要的 ASCII 观察A到Z的 ASCII 码二进制低 5 位恰好是 1 到 26a到z的 ASCII 码二进制低 5 位同样也是 1 到 26。因此ch 31可以把任意大小写字母映射到 1..26规则完全统一无需再调用tolower/unicode.ToLower。元音字母a、e、i、o、u分别是字母表中的第 1、5、9、15、21 个字母。依据「从集合论到位运算」的通用套路可以把元音集合编码为一个整数2^1 2^5 2^9 2^15 2^21 2130466 0x208222于是「判断某字符是否元音」变成一次纯位运算is_vowel(ch) (VOWEL_MASK (ch 31)) 1ch 31得到字母序号后右移掩码最低位若为 1 即说明该序号对应的字母在元音集合中。相比逐字符c a || ... || c u的判断链这种方式更紧凑也更适合在循环中被编译器优化。3.2 完整代码7 语言class Solution: def sortVowels(self, s: str) - str: VOWEL_MASK 0x208222 is_vowel lambda ch: VOWEL_MASK (ord(ch) 31) 1 vowels sorted(filter(is_vowel, s)) t list(s) # str 无法修改转成 list j 0 for i, ch in enumerate(t): if is_vowel(ch): t[i] vowels[j] # 填空 j 1 return .join(t)class Solution { public String sortVowels(String S) { final int VOWEL_MASK 0x208222; char[] s S.toCharArray(); byte[] vowels new byte[s.length]; // 比 StringBuilder 快 int k 0; for (char ch : s) { if ((VOWEL_MASK (ch 31) 1) 0) { vowels[k] (byte) ch; } } Arrays.sort(vowels, 0, k); k 0; for (int i 0; i s.length; i) { if ((VOWEL_MASK (s[i] 31) 1) 0) { s[i] (char) vowels[k]; } } return new String(s); } }class Solution { public: string sortVowels(string s) { const int VOWEL_MASK 0x208222; string vowels; for (char ch : s) { if (VOWEL_MASK (ch 31) 1) { // ch 是元音 vowels ch; } } ranges::sort(vowels); int j 0; for (char ch : s) { if (VOWEL_MASK (ch 31) 1) { // ch 是元音 ch vowels[j]; } } return s; } };#define VOWEL_MASK 0x208222 int cmp(const void* a, const void* b) { return *(char*)a - *(char*)b; } char* sortVowels(char* s) { int n strlen(s); char* vowels malloc(n * sizeof(char)); int k 0; for (int i 0; i n; i) { if (VOWEL_MASK (s[i] 31) 1) { vowels[k] s[i]; } } qsort(vowels, k, sizeof(char), cmp); k 0; for (int i 0; i n; i) { if (VOWEL_MASK (s[i] 31) 1) { s[i] vowels[k]; } } free(vowels); return s; }func sortVowels(s string) string { const vowelMask 0x208222 vowels : []byte{} for _, ch : range s { if vowelMask(ch31)1 0 { // ch 是元音 vowels append(vowels, byte(ch)) } } slices.Sort(vowels) t : []byte(s) j : 0 for i, ch : range t { if vowelMask(ch31)1 0 { // ch 是元音 t[i] vowels[j] j } } return string(t) }var sortVowels function(s) { const VOWEL_MASK 0x208222; const vowels []; for (const ch of s) { if (VOWEL_MASK (ch.charCodeAt(0) 31) 1) { vowels.push(ch); } } vowels.sort(); const t s.split(); let j 0; for (let i 0; i t.length; i) { if (VOWEL_MASK (t[i].charCodeAt(0) 31) 1) { t[i] vowels[j]; } } return t.join(); };impl Solution { pub fn sort_vowels(s: String) - String { const VOWEL_MASK: u32 0x208222; let mut vowels s.bytes() .filter(|ch| VOWEL_MASK (ch 31) 1 0) .collect::Vec_(); vowels.sort_unstable(); let mut s s.into_bytes(); let mut j 0; for ch in s.iter_mut() { if VOWEL_MASK (*ch 31) 1 0 { *ch vowels[j]; j 1; } } unsafe { String::from_utf8_unchecked(s) } } }仓库中的 b.go 第 33-52 行即为sortVowels2的 Go 实现与上文 Go 代码完全一致仅将常量名改为小写vowelMask。四、写法三计数排序优化到 O(n)更进一步元音字符集合很小最多 10 个完全不必排序整个数组只需统计每个元音字符的出现次数再按 ASCII 顺序依次「消耗」次数即可。这是原文档推荐的最终写法。核心技巧在于用一个从A出发的游标j当cnt[j] 0时向后推进到大写Z后折回小写a找到下一个仍有剩余次数的元音填入当前空位并cnt[j]--。class Solution: def sortVowels(self, s: str) - str: VOWELS AEIOUaeiou cnt Counter(ch for ch in s if ch in VOWELS) it iter(VOWELS) cur next(it) t list(s) # str 无法修改转成 list for i, ch in enumerate(t): if ch in VOWELS: if cnt[cur] 0: # 找下一个出现次数大于 0 的元音字母 cur next(c for c in it if cnt[c]) t[i] cur cnt[cur] - 1 return .join(t)class Solution { public String sortVowels(String S) { final int VOWEL_MASK 0x208222; char[] s S.toCharArray(); int[] cnt new int[u 1]; for (char ch : s) { if ((VOWEL_MASK (ch 31) 1) 0) { cnt[ch]; } } int j A; for (int i 0; i s.length; i) { if ((VOWEL_MASK (s[i] 31) 1) 0) { continue; } // 找下一个出现次数大于 0 的元音字母 while (cnt[j] 0) { j j Z ? a : j 1; } s[i] (char) j; cnt[j]--; } return new String(s); } }class Solution { public: string sortVowels(string s) { const int VOWEL_MASK 0x208222; int cnt[u 1]{}; for (char ch : s) { if (VOWEL_MASK (ch 31) 1) { cnt[ch]; } } char j A; for (char ch : s) { if ((VOWEL_MASK (ch 31) 1) 0) { continue; } // 找下一个出现次数大于 0 的元音字母 while (cnt[j] 0) { j j Z ? a : j 1; } ch j; cnt[j]--; } return s; } };#define VOWEL_MASK 0x208222 char* sortVowels(char* s) { int cnt[z 1] {}; for (int i 0; s[i]; i) { if (VOWEL_MASK (s[i] 31) 1) { cnt[s[i]]; } } char j A; for (int i 0; s[i]; i) { if ((VOWEL_MASK (s[i] 31) 1) 0) { continue; } // 找下一个出现次数大于 0 的元音字母 while (cnt[j] 0) { j j Z ? a : j 1; } s[i] j; cnt[j]--; } return s; }func sortVowels(s string) string { const vowelMask 0x208222 cnt : [u 1]int{} for _, ch : range s { if vowelMask(ch31)1 0 { cnt[ch] } } t : []byte(s) j : byte(A) for i, ch : range t { if vowelMask(ch31)1 0 { continue } // 找下一个出现次数大于 0 的元音字母 for cnt[j] 0 { if j Z { j a } else { j } } t[i] j cnt[j]-- } return string(t) }var sortVowels function(s) { const VOWEL_MASK 0x208222; const cnt Array(u.charCodeAt(0) 1).fill(0); for (const ch of s) { const c ch.charCodeAt(0); if (VOWEL_MASK (c 31) 1) { cnt[c]; } } const t s.split(); const ordZ Z.charCodeAt(0); let j A.charCodeAt(0); for (let i 0; i t.length; i) { if ((VOWEL_MASK (t[i].charCodeAt(0) 31) 1) 0) { continue; } // 找下一个出现次数大于 0 的元音字母 while (cnt[j] 0) { j j ordZ ? a.charCodeAt(0) : j 1; } t[i] String.fromCharCode(j); cnt[j]--; } return t.join(); };impl Solution { pub fn sort_vowels(s: String) - String { const VOWEL_MASK: u32 0x208222; let mut cnt [0; z as usize 1]; for ch in s.bytes() { if (VOWEL_MASK (ch 31)) 1 0 { cnt[ch as usize] 1; } } let mut s s.into_bytes(); let mut j 0; for ch in s.iter_mut() { if VOWEL_MASK (*ch 31) 1 0 { continue; } // 找下一个出现次数大于 0 的元音字母 while cnt[j as usize] 0 { if j bZ { j ba; } else { j 1; } } *ch j; cnt[j as usize] - 1; } unsafe { String::from_utf8_unchecked(s) } } }该写法即仓库 b.go 中第 54-81 行的最终版sortVowels测试实际调用的正是这一版。五、三种写法复杂度对比写法时间复杂度空间复杂度说明写法一收集排序填空O(n log n)O(n)n 为字符串长度排序是唯一瓶颈写法二位掩码判定 排序O(n log n)O(n)仅优化了元音判定方式复杂度不变写法三计数排序O(n Σ)O(Σ)Σ 为字符集合大小可取 10 / 52 / 128其中写法三的时间复杂度中n是s的长度Σ 10 或 52 或 128是字符集合的大小空间复杂度 O(Σ) 仅与统计数组大小有关与输入长度无关。六、仓库配套源码、测试数据与自动化验证这道题在仓库中的完整配套体现了 codeforces-go 项目「题解 可运行实现 自动化测试」的标准组织方式题解文档leetcode/biweekly/109/b/README.md即本文主体来源Go 实现leetcode/biweekly/109/b/b.go包含sortVowels1写法一、sortVowels2写法二与最终版sortVowels写法三三个函数测试数据leetcode/biweekly/109/b/b.txt每两行一组「输入/期望输出」覆盖普通用例与无元音用例测试文件leetcode/biweekly/109/b/b_test.go调用testutil.RunLeetCodeFuncWithFile从b.txt读取用例驱动sortVowels进行断言比对测试驱动leetcode/testutil/leetcode.go 中的RunLeetCodeFuncWithFile负责按函数入参/返回值个数切分数据行、反射调用被测函数并逐用例断言还支持「无尽对拍」与 TLE 检测等调试能力用例生成器copypasta/template/leetcode/generator.go 会从力扣比赛页面自动抓取题目、默认代码与样例生成x.go/x_test.go/x.txt三件套其入口测试见 copypasta/template/leetcode/generator_test.go。本地运行验证方式仓库为只读研究用途你可以在本地git clone后进入对应目录执行cd leetcode/biweekly/109/b go test -run Test_b -v测试会遍历b.txt中的每个用例将sortVowels的实际输出与期望输出比对含超时检测逻辑见 leetcode.go 的isTLE实现。若把b_test.go中的targetCaseNum改为-1则只跑最后一个用例方便单步调试。七、技巧迁移与总结本题的价值不止于 AC 本身更在于两个可复用技巧ch 31大小写统一映射利用 ASCII 码低 5 位与字母序号的对应关系把「字符属于某集合」的判断统一到 1..26 的数字域消除大小写分支集合的整数编码位掩码把小集合编码为2^1 2^5 2^9 2^15 2^21这样的整数用(mask k) 1实现 O(1) 集合成员判定。该套路在字符分类、状态压缩、子集枚举等场景同样适用。从工程视角看本题的三种写法恰好构成一条完整的优化链条先保证正确写法一再用位运算简化热路径写法二最后利用字符集小的特性把排序降为计数写法三复杂度从O(n log n)降到O(n Σ)。配合仓库中自动生成的测试用例与测试框架每一种写法都能被立即验证——这正是算法题解从「思路」落地为「可信代码」的完整范式。赞分享科学计算【免费下载链接】codeforces-go算法竞赛模板库 by 灵茶山艾府 项目地址https://gitcode.com/GitHub_Trending/co/codeforces-go点击查看免费下载相关推荐codeforces-go 仓库实战力扣双周赛 104「英雄的力量」贡献法递推题解全解析codeforces go 仓库实战力扣双周赛 104「英雄的力量」贡献法递推题解全解析 导读 本篇技术指南以仓库中 双周赛 104 第四题题解 https:科学计算codeforces-go 仓库题解精读力扣双周赛 107 B 题「构造最长的新字符串」的数学公式与状态机记忆化搜索codeforces go 仓库题解精读力扣双周赛 107 B 题「构造最长的新字符串」的数学公式与状态机记忆化搜索 本篇题解以开源算法竞赛模板库 codef科学计算MXNet C 包推理实战指南从 ImageNet 图像分类到 RNN 情感分析MXNet C 包推理实战指南从 ImageNet 图像分类到 RNN 情感分析 导读 本文基于 cpp package/example/inferenc科学计算上一篇Hyperledger Fabric Peer 官方镜像使用指南容器化部署、配置挂载与数据卷详解下一篇Taichi C API 的 Vulkan 后端互操作指南运行时、缓冲与图像的共享资源导入导出创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考