ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

freeCodeCamp 每日编程挑战 229:基于字符宽度的文本截断(Truncate the Text 2)设计与完整解析

freeCodeCamp 每日编程挑战 229:基于字符宽度的文本截断(Truncate the Text 2)设计与完整解析 freeCodeCamp 每日编程挑战 229基于字符宽度的文本截断Truncate the Text 2设计与完整解析【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp本文围绕 freeCodeCamp 课程中 Daily Coding ChallengesJavaScript第 229 题Truncate the Text 2展开先完整还原题目规则与字符宽度表再逐案验算全部 5 个测试用例的宽度然后对官方参考实现做逐行剖析并结合仓库中的课程结构配置、挑战类型定义与数据校验源码说明这类挑战题在 freeCodeCamp 工程体系中的组织方式。读完本文你可以掌握按不等宽字符做预算式贪心截断的完整实现思路以及字符分组表、首匹配优先、边界条件不超过 50 单位且取最接近值三个关键设计点。一、题目定位这道题在仓库中的位置题目原文档位于每日编码挑战 JavaScript 模块下题目文件curriculum/challenges/english/blocks/daily-coding-challenges-javascript/69a890af247de743333bd4d2.md文件头 YAML 声明了id: 69a890af247de743333bd4d2、title: Challenge 229: Truncate the Text 2、challengeType: 28。模块的结构定义文件 curriculum/structure/blocks/daily-coding-challenges-javascript.json 中可以看到这道题的排布上下文dashedName: daily-coding-challenges-javascripthelpCategory: JavaScriptblockLayout: legacy-challenge-list并开启了usesMultifileEditor: true与disableLoopProtectTests: truechallengeOrder数组中本挑战id69a890af247de743333bd4d2紧跟在 Challenge 228: Movie Nightid69a890af247de743333bd4d1之后、Challenge 230: Pascals Triangle Row 之前是模块内第 229 道挑战。challengeType: 28的含义可以在共享包中确认。packages/shared/src/config/challenge-types.ts 第 30 行定义了const dailyChallengeJs 28;同文件还给出了三处与本题渲染/提交行为相关的映射viewTypes中[dailyChallengeJs]: classic第 134 行即题目使用经典代码编辑器视图呈现submitTypes中[dailyChallengeJs]: tests第 175 行即提交后走运行测试断言的判定流程第 182-206 行提供了getIsDailyCodingChallenge()与getDailyCodingChallengeLanguage()将 28/29 两类挑战映射到javascript/python语言用于每日挑战的数据分发。值得一提的是本模块存在一道前身题目 Challenge 178: Truncate the Text69738771fb5a7b8b24cca2a4.md它按固定字符数截断——不超过 20 个字符原样返回超过则截取前 17 个字符再加...。而 Challenge 229 的 2 意味着把度量单位从字符个数升级为字符宽度难度体现在不等宽度量下的预算分配这也是本文的重点。二、完整题目规则与字符宽度表原题 description 部分给出的规则如下完整保留给定一个字符串返回一个截断后的新字符串使得所有字符的总宽度不超过 50 个单位。每个字符都有特定的宽度| 字母 | 宽度 | | - | - | |ilI| 1 | |fjrt| 2 | |abcdeghkmnopqrstuvwxyzJL| 3 | |ABCDEFGHKMNOPQRSTUVWXYZ| 4 |上表涵盖了所有大小写字母。此外空格 宽度为 2句号.宽度为 1如果给定字符串宽度不超过 50 个单位原样返回该字符串否则截断该字符串并在末尾加上三个句号...使总宽度包含这三个句号在不超过 50 的前提下尽可能接近 50。对这张宽度表做两点说明它们是正确解题的前提四组字母恰好覆盖 52 个英文字母。小写字母中i、l宽 1f、j、r、t宽 2其余 20 个小写字母宽 3大写字母中I宽 1J、L宽 3其余 23 个大写字母宽 4。这与真实等宽字体中窄字符i、l、f、j、r、t偏细、宽字符如W、M偏粗的排版直觉一致。宽度表中存在重叠字符r和t同时出现在宽度 2 组fjrt和宽度 3 组abcdeghkmnopqrstuvwxyzJL中。参考实现用按组遍历、首个命中生效的策略消解了这种歧义——由于宽度 2 组在遍历顺序上先于宽度 3 组r和t的最终宽度是 2。这一点可以反向验证若t被算作宽 3测试用例The fast striped zebra的期望输出就不可能保留末尾的z详见下节验算。三、测试用例与逐案宽度验算题目 hints 部分给出 5 条断言原文完整保留assert.equal(truncateText(The quick brown fox), The quick brown f...); assert.equal(truncateText(The silky smooth sloth), The silky smooth s...); assert.equal(truncateText(THE LOUD BRIGHT BIRD), THE LOUD BRIG...); assert.equal(truncateText(The fast striped zebra), The fast striped z...); assert.equal(truncateText(The big black bear), The big black bear);下面用宽度表对前两条做逐字符验算截断预算见第四节正文宽度上限 47 省略号 3用例 1The quick brown fox| 片段 | 宽度累加 | | - | - | |The 433 | 10 | |quick 233133 | 1015 25 | |brown 232343 | 2517 42 | |f 22 | 424 46 | | 下一个o宽 3将使 463 49 47 | 停止 |保留The quick brown f宽 46加上...宽 3总宽 49 ≤ 50且再多一个字符都会超限输出The quick brown f...与断言一致。用例 3THE LOUD BRIGHT BIRD全大写展示宽 4 字符的预算消耗速度THE12LOUD141630BRI212143G3 后 46重新累加T4H4E4 12空格 2 → 14L4O4U4D4 → 30空格 2 → 32B4 → 36R4 → 40I1 → 41G3 → 44下一个H宽 4使 44448 47停止。保留THE LOUD BRIG宽 44 省略号 47输出THE LOUD BRIG...与断言一致。用例 4 隐含确认了t的宽度为 2The fast striped zebra 中The10 fast1222332 24f2a3s3t2striped23221333 43z23 48按序空格 2 → 45z3 → 48 47应为 46 后保留z45348 超限——重新精确累加The1012f14a17s20t2224s27t29r31i32p35e38d4143z46 ≤ 47 保留下一个e3使 46349 47停止。保留The fast striped z 省略号 49输出The fast striped z...与断言一致而如果把t算成宽 3累加会在z之前提前 2 个单位耗尽预算无法保留末尾的z从而反证了t2的首匹配规则。用例 5 是不截断分支The big black bear 总宽 48 ≤ 50原样返回。四、参考实现逐行剖析题目--solutions--部分给出的官方参考实现完整保留如下function truncateText(str) { const MAX_WIDTH 50; const ELLIPSIS ...; const ELLIPSIS_WIDTH 3; const TRUNCATE_LIMIT MAX_WIDTH - ELLIPSIS_WIDTH; const charGroups { ilI.: 1, fjrt : 2, abcdeghkmnopqrstuvwxyzJL: 3, ABCDEFGHKMNOPQRSTUVWXYZ: 4 }; function getCharWidth(char) { for (const key in charGroups) { if (key.includes(char)) return charGroups[key]; } return 3; } function stringWidth(str) { let totalWidth 0; for (const char of str) { totalWidth getCharWidth(char); } return totalWidth; } if (stringWidth(str) MAX_WIDTH) return str; let result ; let totalWidth 0; for (const char of str) { const charWidth getCharWidth(char); if (totalWidth charWidth TRUNCATE_LIMIT) break; result char; totalWidth charWidth; } return result ELLIPSIS; }按结构拆解其四个设计决策1. 预算先扣减TRUNCATE_LIMIT MAX_WIDTH - ELLIPSIS_WIDTH 47。截断结果必须正文 三个句号整体不超过 50所以正文部分只能占用 50 − 3 47 个单位。这一行是整个算法的核心变换把带后缀的总宽约束转化为对正文的纯宽度预算后续循环只需与 47 比较。2. 字符宽度表用分组字符串 首匹配表达而不是 52 个单字符条目。charGroups把同宽字符合并为四个字符串键getCharWidth按for...in的插入顺序遍历、key.includes(char)命中即返回。这个结构有两个细节值得注意句号.被直接拼进宽度 1 组的键ilI.空格被拼进宽度 2 组fjrt 与题目空格宽 2、句号宽 1的补充规则一一对应避免了额外的特判分支组间存在r/t重叠时先遍历的宽度 2 组胜出如第三节用例 4 的验算所依赖的。若追求严谨可改为按字符查表或保证各组互斥但就本题字符集而言首匹配策略与题目断言完全自洽。兜底分支return 3在题目约束下仅字母、空格、句号实际不会触发属于防御性写法。3. 双阶段流程先全量度量再贪心填充。stringWidth(str) MAX_WIDTH时直接原样返回用例 5 的分支否则进入单次扫描的贪心循环逐字符尝试纳入结果一旦totalWidth charWidth TRUNCATE_LIMIT立即break。由于扫描是前缀式的且宽度全部为正数该循环天然保证保留的正文是所有宽度不超过 47 的最长前缀因此加上省略号后的总宽是不超过 50 的最接近值——这正是题目as close as possible to 50 units without going over的严格实现。4. 边界比较符是严格大于而非大于等于。if (totalWidth charWidth TRUNCATE_LIMIT) break;允许正文恰好填到 47如用例 3 的THE LOUD BRIG44 3 47若某串正文恰好 47加省略号正好 50。若误写为结果会无谓地短一个字符。题目断言中The silky smooth s...的正文宽度恰为 47验算The10silky23113323smooth233332342s2347是这条边界用法的直接证据。五、工程侧视图这道题如何被组织、校验与分发从仓库结构看一道每日编码挑战题在 freeCodeCamp 体系中的生命周期涉及三层文件课程内容层本文主体所在Markdown 挑战文件 模块结构 JSON。--description--渲染为题目正文--hints--中的每条assert.equal(...)代码块即为该题的判题断言--seed--是编辑器初始代码本例为直接return str;的桩函数--solutions--供 SolutionViewer 展示参考答案。课程元数据层challengeType: 28由 packages/shared/src/config/challenge-types.ts 统一解释为dailyChallengeJs并由此决定视图classic与提交方式testscurriculum/schema/challenge-schema.js 第 180 行的Joi.number().min(0).max(33).required()则约束了课程文件中challengeType的合法取值范围0-33。运行时数据层每日挑战在数据库中的结构由客户端校验器描述——client/src/utils/daily-coding-challenge-validator.ts 中一条挑战数据必须包含id、challengeNumber从 1 起的整数、title、date、description以及javascript与python两个语言子集每个语言子集都要求tests由texttestString组成可推断提示断言在此被编译为可执行测试串和challengeFilesfileKeycontents的编辑器文件。API 侧的取数端点位于 api/src/daily-coding-challenge/routes/daily-coding-challenge.ts其 README.md 说明每日挑战信息通过独立端点获取提交仍走主 API。需要说明的前提是本文讨论的是课程仓库中的静态题目定义curriculum/challenges/english/blocks/daily-coding-challenges-javascript/下的英文原版同一编号挑战的多语言版本位于对应语言目录且模块当前带有isUpcomingChange: true标记表示该块处于即将上线/变更的课程排期中。六、小结与可迁移的实现要点这道 Truncate the Text 2 虽然规模很小但完整覆盖了一个实用类问题的全部要素明确的分档宽度表、带后缀的复合宽度约束、不超过且最接近的优化目标。其解法可以抽象为三步模板同样适用于预算内最大化前缀一类的变体题先扣除固定占用省略号 3 单位得到正文预算 47用分组表把 O(1) 语义的查宽操作压进一次遍历并明确重叠字符的消歧策略本题为首匹配等价于r/t取宽 2前缀贪心 严格大于比较保证结果既是不超过的最优解也是所有合法解中最长的那个。对照模块中的前身题目 Challenge 178: Truncate the Text固定 20 字符截断可以看出出题意图的递进从按字符数到按字符宽度唯一的实质变化就是引入了不等宽度量——而一旦有了度量slice(0, 17)式的下标截断就不再成立必须换成逐字符累加的预算扫描。这正是本题值得单独作为2来练手的核心原因。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表