news 2026/9/10 13:29:41

freeCodeCamp 每日编程挑战解析:Challenge 83 Signature Validation(签名校验算法实现)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
freeCodeCamp 每日编程挑战解析:Challenge 83 Signature Validation(签名校验算法实现)

freeCodeCamp 每日编程挑战解析:Challenge 83 Signature Validation(签名校验算法实现)

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

本篇技术指南围绕 freeCodeCamp 开源课程中的每日编程挑战(Daily Coding Challenges)第 83 题展开,完整讲解「Signature Validation(签名校验)」一题的编码规则、验证目标、官方测试用例与源码级解法。你将掌握如何用 JavaScript 实现一套基于字母数值求和的签名校验函数,理解charCodeAtfor...of在字符遍历中的应用,并了解该挑战在 freeCodeCamp 课程仓库中的落地形态(Markdown 挑战文件、测试驱动校验与 Python 对照实现)。

挑战背景:它来自哪里

本挑战位于课程仓库的daily-coding-challenges-javascript块(block)内,源文件为 curriculum/challenges/english/blocks/daily-coding-challenges-javascript/68e39ed6106dac2f0a98fd65.md,文件头部元信息:

id: 68e39ed6106dac2f0a98fd65 title: "Challenge 83: Signature Validation" challengeType: 28 dashedName: challenge-83

其中challengeType: 28在 packages/shared/src/config/challenge-types.ts 中被定义为dailyChallengeJs,即「每日 JavaScript 挑战」专用类型;该挑战同时存在同 id 的 Python 版本(challengeType: 29),对应文件为 curriculum/challenges/english/blocks/daily-coding-challenges-python/68e39ed6106dac2f0a98fd65.md,两者共用同一套题目描述与测试数据。块的元信息由 curriculum/structure/blocks/daily-coding-challenges-javascript.json 维护,其中记录了本块共 248 余道挑战的challengeOrder,Challenge 83 前后分别是「Challenge 82: SpOoKy~CaSe」与「Challenge 84: Infected」。

题目描述:什么是签名校验

给定三个输入:

  • 一个消息字符串message
  • 一个密钥字符串key
  • 一个签名数字signature

需要判断该签名是否有效。这里的「签名」并非密码学意义上的哈希签名,而是一套简单编码规则下的数值总和:

  1. 消息与密钥中的字母各有对应数值:
    • az(小写)分别取值126
    • AZ(大写)分别取值2752
  2. 其余所有字符(数字、标点、空格等)不产生任何数值(值为 0)。
  3. 签名的计算方式为:消息所有字符数值之和 + 密钥所有字符数值之和

最终只需比对「计算出的签名」与「提供的签名」是否相等即可。

这一规则恰好对应字母在 Unicode 字符集中的连续排布:小写字母从a(97)到z(122),大写字母从A(65)到Z(90),因此可以通过码点差值把字母映射为 1–26 / 27–52 的整数,这正是后续解法的核心。

手动推演:官方示例"foo""bar"

题目给出的示例可以完整拆解为:

f (6) + o (15) + o (15) = 36 b (2) + a (1) + r (18) = 21 36 + 21 = 57

即:

  • f是第 6 个小写字母 →6o是第 15 个小写字母 →15,消息"foo"合计36
  • b2a1r18,密钥"bar"合计21
  • 两者相加得到签名57,因此verify("foo", "bar", 57)返回true

官方测试用例(hints)

原文档共给出 6 个断言,覆盖了大小写混合、标点空格混入、长文本等场景:

调用期望结果说明
verify("foo", "bar", 57)true官方示例,全部小写
verify("foo", "bar", 54)false签名差 3,验证不通过
verify("freeCodeCamp", "Rocks", 238)true大小写混合
verify("Is this valid?", "No", 210)false含空格与标点,需忽略非字母
verify("Is this valid?", "Yes", 233)true同一消息、不同密钥与签名
verify("Check out the freeCodeCamp podcast,", "in the mobile app", 514)true长文本 + 逗号等标点

其中第 4、5 条尤其能检验对「非字母字符值为 0」这一规则的理解:消息"Is this valid?"中含 3 个空格、1 个问号与 2 个单词边界,计算时必须全部跳过,只累加字母。例如第 5 条中"Is this valid?"的各字母值之和为I(35) + s(19) + t(20) + h(8) + i(9) + s(19) + v(22) + a(1) + l(12) + i(9) + d(4) = 158"Yes"Y(51) + e(5) + s(19) = 75,合计233,正好命中。

测试断言本身以 Chai 风格写入挑战文件的--hints--段,例如:

assert.isTrue(verify("foo", "bar", 57)); assert.isFalse(verify("foo", "bar", 54));

起始代码(seed)

挑战要求基于以下函数骨架补全实现,verify接收三个参数并返回布尔值:

function verify(message, key, signature) { return message; }

注意初始代码直接return message(返回一个字符串),必然无法通过任何断言,这是有意为之——课程测试基建会验证「初始代码必须让测试失败」,具体见后文「测试如何验证你的代码」。

官方解法逐行拆解(JavaScript)

--solutions--段给出的参考答案将问题拆成三个层次:单字符求值、字符串求和、签名比对。

function verify(message, key, signature) { function charValue(ch) { if (ch >= 'a' && ch <= 'z') return ch.charCodeAt(0) - 'a'.charCodeAt(0) + 1; if (ch >= 'A' && ch <= 'Z') return ch.charCodeAt(0) - 'A'.charCodeAt(0) + 27; return 0; } function computeSum(str) { let sum = 0; for (let ch of str) { sum += charValue(ch); } return sum; } const total = computeSum(message) + computeSum(key); return total === signature; }

关键实现点:

  1. 字符数值映射ch.charCodeAt(0)返回字符的 Unicode 码点。'a'.charCodeAt(0)为 97,因此charCodeAt(0) - 97 + 1a映射为 1、z映射为 26;大写分支以'A'(65)为基准再加 27,得到 27–52。字符串比较ch >= 'a' && ch <= 'z'能正确判定单个字母字符的区间。
  2. 逐字符求和for...of按 Unicode 码点顺序遍历字符串中的每个字符(对本题的 ASCII 输入等价于逐字符遍历),把每个字符的charValue累加。非字母字符落入return 0分支,自然被忽略。
  3. 严格相等比对total === signature用严格相等运算符,避免隐式类型转换引入的意外结果;签名以数字类型传入,total亦为数字,二者类型一致。

复杂度分析:假设m为消息长度、k为密钥长度,算法对每个字符恰好访问一次,时间复杂度为O(m + k);仅使用常数级额外空间,空间复杂度为O(1)

Python 对照解法

同一挑战的 Python 版本(curriculum/challenges/english/blocks/daily-coding-challenges-python/68e39ed6106dac2f0a98fd65.md)逻辑完全一致,仅将charCodeAt换成ord

def verify(message, key, signature): def charValue(ch) -> int: if 'a' <= ch <= 'z': return ord(ch) - ord('a') + 1 elif 'A' <= ch <= 'Z': return ord(ch) - ord('A') + 27 else: return 0 def compute_sum(s): return sum(charValue(ch) for ch in s) total = compute_sum(message) + compute_sum(key) return total == signature

Python 版本利用生成器表达式sum(charValue(ch) for ch in s)一步完成求和,测试断言则通过runPython内嵌unittest.TestCase().assertIs(...)实现,例如:

({test: () => { runPython(` from unittest import TestCase TestCase().assertIs(verify("foo", "bar", 57), True)`) }})

仓库如何验证你的代码:测试基建剖析

该挑战的测试并非只在网页端运行,课程仓库本身也对其做了一整套自动化验证,可以从 curriculum/src/test/daily-challenges.test.js 看到:

  • 测试会加载dev-playground超级块下所有挑战,并按 block 过滤出daily-coding-challenges-javascriptdaily-coding-challenges-python
  • 断言两个语言版本挑战数量一致(每题都有 JS 与 Python 双版本);
  • 逐一比对同下标挑战的idtitledescription与测试条数,确保双语版本严格对齐(当前语言非英文时跳过标题/描述比对)。

而 curriculum/src/test/test-challenges.js 中的通用流程对每个挑战执行两项关键检查:

  1. 初始内容必须让测试失败:把挑战文件的--seed-contents--(此处即return message;骨架)作为考生代码跑一遍官方 hints,断言其结果必须是失败的,防止题目答案被误写进种子代码;
  2. 参考答案必须通过全部测试:将--solutions--段替换进代码后重跑同一组测试断言,保证官方解法与 hints 完全自洽。

也就是说,Challenge 83 的 6 条断言会同时被用来「拒绝骨架代码、接受参考解法」,形成一套闭环的挑战质量保障机制。从challengeType映射看,dailyChallengeJs使用classic类型的测试 runner(见 packages/shared/src/config/challenge-types.ts),即直接在浏览器环境中执行断言函数并检查返回值。

边界情况与易错点

结合编码规则与测试用例,实践时需特别注意:

  • 大小写区分a(1)与A(27)数值不同,不能统一转为小写再映射,否则"Rocks"这类含大写字母的用例会算错;
  • 非字母字符:空格、问号、逗号、数字等一律按 0 处理,且不能被跳过导致索引错位——只需正常遍历并在求值时返回 0 即可;
  • 返回值类型:函数必须返回布尔值true/false,不能返回数字差值或字符串;
  • 比较运算:JavaScript 分支中ch >= 'a' && ch <= 'z'依赖单字符与单字符的字典序比较,若对多字符子串使用会得到与预期不符的结果;
  • 大字符串输入:题目包含长文本用例(如最后一条含逗号的 40 字符消息),但数值规模仍在Number安全整数范围内,无需大数处理。

延伸思考:从求和到真实校验

本题的「签名」是字母数值求和,属于教学性的简化模型。从中可以延伸思考真实签名机制的两个核心原则,它们在本题中均有雏形:

  • 确定性与可复现:相同的消息 + 密钥必须计算出相同的签名,因此逐字符映射规则必须无歧义(本题用字符区间而非字符集枚举保证);
  • 对篡改敏感:改动任何字母都会改变总和,从而改变签名——第 2 条测试57 → 54正是因为求和结果与给定签名不一致而判定无效。

现实中的 HMAC、RSA 签名等方案在此模型上叠加了密钥分散、不可逆哈希与抗碰撞设计,但其「用双方共知的材料生成可验证的指纹」的思想一脉相承。

小结

Challenge 83「Signature Validation」是一道典型的基础算法题:掌握字符到数值的区间映射、字符串逐字符遍历与累计求和,即可在O(m + k)时间内完成签名校验。通过本文你不仅掌握了题目本身的解法(含官方 JS 答案与 Python 对照),还了解了它在 freeCodeCamp 课程仓库中的完整生命周期——从 挑战 Markdown 文件 的 front-matter 元信息,到 challenge-types.ts 的类型定义,再到 test-challenges.js 的「骨架必败、解答必过」双端验证机制。若想进一步探索同系列题目,可直接查看 daily-coding-challenges-javascript 块目录 下的其他挑战文件,或阅读 daily-coding-challenges-javascript.json 中的完整挑战清单。

【免费下载链接】freeCodeCampfreeCodeCamp.org's open-source codebase and curriculum. Learn math, programming, and computer science for free.项目地址: https://gitcode.com/GitHub_Trending/fr/freeCodeCamp

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 13:29:38

解决TDLib线程安全痛点:ThreadIdGuard检查失败的完整方案

解决TDLib线程安全痛点&#xff1a;ThreadIdGuard检查失败的完整方案 你是否在集成TDLib开发Telegram客户端时遇到过随机崩溃&#xff1f;是否被"ThreadIdGuard check failed"错误困扰&#xff1f;本文将从问题根源出发&#xff0c;提供一套完整的诊断与解决方案&am…

作者头像 李华
网站建设 2026/9/10 13:28:30

农作物病虫害识别:PyTorch迁移学习图像分类实战

简介&#xff1a;一份基于Python的农作物病虫害识别分类项目完整资源&#xff0c;适合机器学习初学者、农业院校学生或有图像识别需求的农业技术人员参考。项目围绕AI Challenger植物病害识别赛题展开&#xff0c;覆盖数据收集与预处理、特征工程、模型选择、训练与评估完整流程…

作者头像 李华