行线索验证器)
freeCodeCamp 每日编程挑战 Challenge 362用 JavaScript 实现 Nonogram数织行线索验证器【免费下载链接】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 Challenges - JavaScript中的 Challenge 362「Nonogram Validator」为主体完整讲解数织线索nonogram clue的验证问题从题目规则、全部测试用例的语义分析到官方参考解法的单趟游程run-length扫描原理并结合仓库源码挑战类型定义、区块配置、每日挑战种子脚本与客户端渲染路径说明该挑战在 freeCodeCamp 课程体系中的定位与运行机制。读完后你将能够独立写出通过全部测试用例的isValidNonogram实现并理解这道题背后先提取游程、再逐项比对这一可复用的算法模式。题目描述与规则该挑战的原始题目定义在 Challenge 362 题目文件 中要求实现函数isValidNonogram(clue, cells)给定一个线索数字数组clue和一个格子数组cells判断格子是否满足该 nonogram 线索。线索是一个数字数组按顺序表示连续填充格子的长度。例如线索[3, 2]意味着应有 3 个连续填充的格子随后是 2 个连续填充的格子两组之间至少隔开一个空格。行cells是一个由 1填充和 0空格组成的数组。题目附带两个关键约束是理解全部测试用例的钥匙顺序性游程必须按 clue 中数字出现的顺序依次出现顺序打乱即无效严格匹配cells 中出现的每一个 1 都必须恰好被线索覆盖——游程数量、长度、顺序三者必须与 clue 完全一致不能多、不能少、不能偏长。这一点由非官方 seed 代码要求开发者在其基础上补全逻辑暗示了初始实现骨架function isValidNonogram(clue, cells) { // 待补全 return clue; }测试用例全集与逐条解析题目文件# --hints--部分给出了 6 条assert测试它们是本题的验收标准。完整继承如下并逐条说明其考察的边界#调用期望考察点1isValidNonogram([3, 2], [1, 1, 1, 0, 1, 1])true基本正例两个游程3、2精确匹配2isValidNonogram([3, 2], [0, 1, 1, 1, 1, 1])false游程合并陷阱实际只有一个长度 4 的游程而非[3, 2]之间的分隔3isValidNonogram([1, 1, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1])false游程数量不符实际有 5 个长度为 1 的游程线索只有 4 个4isValidNonogram([1, 1, 1, 1], [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0])true间距可以大于 1线索只要求至少一个空格多个连续空格完全合法5isValidNonogram([3, 2, 3], [0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0])true前后可以有空格游程前后的连续 0 不构成多余游程6isValidNonogram([3, 2, 3], [0, 0, 0, 1, 0, 0, 1, 0, 0, 0])false长度不匹配游程数量对了3 个但每个游程长度都是 1与[3, 2, 3]不符对应的断言写法与题目文件一致assert.isTrue(isValidNonogram([3, 2], [1, 1, 1, 0, 1, 1])); assert.isFalse(isValidNonogram([3, 2], [0, 1, 1, 1, 1, 1])); assert.isFalse(isValidNonogram([1, 1, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1])); assert.isTrue(isValidNonogram([1, 1, 1, 1], [0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0])); assert.isTrue(isValidNonogram([3, 2, 3], [0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0])); assert.isFalse(isValidNonogram([3, 2, 3], [0, 0, 0, 1, 0, 0, 1, 0, 0, 0]));可以观察到一个设计意图这 6 条用例分别压住了游程提取与比对逻辑两个环节的典型错误——用例 2 惩罚只数 1 的总数的错误思路[0,1,1,1,1,1]总共有 5 个 1[3,2]也加起来是 5但它必须是两个分离的游程用例 3 惩罚长度和相等就通过的思路用例 6 则惩罚只比较游程个数、不比较长度的思路。参考解法单趟游程扫描 逐项比对题目文件# --solutions--给出的官方参考解法如下function isValidNonogram(clue, cells) { const runs []; let count 0; for (let i 0; i cells.length; i) { if (cells[i] 1) { count; } else if (count 0) { runs.push(count); count 0; } } if (runs.length ! clue.length) return false; return runs.every((run, i) run clue[i]); }其核心是把验证拆成两个阶段阶段一从 cells 中提取游程列表runs。用一个计数器count累计当前连续 1 的长度每当遇到一个 0或走到数组末尾且count 0时说明一段游程结束把count压入runs并清零。这个遇到分隔符才结算上一段的写法是典型的游程提取模式与解析 CSV 分字段、解析词频分词是同一类技巧。阶段二与 clue 严格比对。先比长度runs.length ! clue.length再用Array.prototype.every逐项比较run clue[i]。长度先行短路避免在游程数量不同时的无意义逐项比较every的短路求值也保证了首项不符时立即返回false。一个值得细究的细节是循环条件写作i cells.length而不是惯用的i cells.length。当i cells.length时cells[i]为undefined不等于 1于是走else if分支若此时count 0即格子数组以 1 结尾最后一段游程也会被正确结算。若不写这个越界的哨兵迭代[3, 2]对[1, 1, 1, 0, 1, 1]这样以 1 结尾的输入就会漏掉末尾游程、错误地返回false。换言之这一处恰好覆盖了行尾即隐式空格的边界与数织规则中行边界天然充当分隔的语义一致。解法正确性推演以用例 5[3, 2, 3]对[0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0]走一遍流程前两个 0count保持 0不触发结算count 0不成立因此行首空格不会产生空游程三个 1 后遇 0结算runs [3]两个 0 后两个 1 再遇 0结算runs [3, 2]三个 1 到末尾哨兵迭代结算runs [3, 2, 3]runs.length clue.length 3逐项33, 22, 33返回true。再看用例 6[3, 2, 3]对[0, 0, 0, 1, 0, 0, 1, 0, 0, 0]提取出runs [1, 1, 1]长度虽同为 3但1 ! 3第一个元素即短路返回false。该解法的时间复杂度为 O(n)n 为 cells 长度空间复杂度 O(k)k 为游程数最坏 O(n)单次遍历、无回溯适合在浏览器编辑器中即时运行。从源码结构看这道题在 freeCodeCamp 仓库中的位置题目文件本身是一个每日挑战块中的一个挑战条目。围绕它的仓库证据可以回答三个问题它属于哪个类型、如何被组织、如何被用户看到。1. 挑战类型challengeType: 28即 JavaScript 每日挑战。题目文件 frontmatter 中声明challengeType: 28。在 challenge-types.ts 中常量dailyChallengeJs 28同文件还有dailyChallengePy 29并且viewTypes将 28 映射为classic即使用经典单题编辑器界面渲染submitTypes将 28 映射为tests即完成判定依赖题目内嵌的 assert 测试集也就是上文的 6 条断言getIsDailyCodingChallenge(challengeType)通过判断类型是否为 28/29 来识别每日挑战。同时注意 28 不在hasNoSolution的列表中因此该挑战在界面上允许展示参考答案——这与题目文件自带# --solutions--区块的设计吻合。2. 区块组织365 天中的第 362 题。挑战被登记在 daily-coding-challenges-javascript.json 的challengeOrder中id6a26df95efa55a2524399743标题 Challenge 362: Nonogram Validator。该区块配置了几个与运行行为直接相关的字段usesMultifileEditor: true尽管本题只涉及script.js单文件编辑器仍按多文件布局挂载disableLoopProtectTests: true禁用循环保护类测试钩子允许答案中包含常规for循环参考解法正是循环扫描helpCategory: JavaScript求助分类归入 JavaScript。3. 投放机制从题库到每日一题。从仓库源码看每日挑战的内容投放链路是挑战 Markdown 存放在curriculum/challenges/english/blocks/daily-coding-challenges-javascript/下 → seed-daily-challenges.ts 脚本通过 GraphQL 拉取题库脚本中EXPECTED_CHALLENGE_COUNT 365起始日期固定为 2025-08-11即第 N 题对应起始日 N-1 天把 JavaScript 与 Python 版本合并后 upsert 进DailyCodingChallenges集合 → 客户端 show-daily-coding-challenge.tsx 按月-日路径向 API 请求/daily-coding-challenge/day/{monthDay}经 schema 校验后把数据包装为challengeType: 28的经典挑战dashedName 形如challenge-${challengeNumber}与题目文件的dashedName: challenge-362规则一致最终交给ShowClassic渲染并运行测试。本地运行该脚本的流程记录在 tools/daily-challenges/README.md复制sample.env为.env、安装依赖、启动带 upcoming changes 的主客户端后执行pnpm seed-daily-challenges。这条链路解释了题目文件中的两处元数据约定id是 MongoDB ObjectId 风格的稳定标识跨语言版本、跨数据库引用同一题dashedName: challenge-362则是按题号生成的 URL 友好名。小结Challenge 362 表面上是一道数织小验证题实质考察的是两个通用能力一是把二值序列压缩为游程列表的单趟扫描含行尾哨兵迭代的边界处理二是先比规模、再逐项比较的严格匹配纪律。官方解法 15 行内完成O(n) 时间与空间恰好落在本题区块disableLoopProtectTests: true所允许的常规循环用法内。在仓库中它作为daily-coding-challenges-javascript区块的第 362 条、challengeType: 28JavaScript 每日挑战的一员经由种子脚本按日期投放、由经典编辑器渲染、以内嵌 assert 测试集判定完成——题目文件题目与解答、区块结构区块配置、类型系统challenge-types.ts与投放脚本seed-daily-challenges.ts共同构成了一道每日一题从静态内容到线上交付的完整闭环。【免费下载链接】freeCodeCampfreeCodeCamp.orgs open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考