微信 网站 收费,企业oa系统搭建,公司域名查询,wordpress 浏览缓慢本文记录 CS50x Week 2 Problem Set 的解题过程。这周的主题是数组#xff08;Arrays#xff09;和字符串#xff08;Strings#xff09;#xff0c;我们将通过四道题目深入理解这些核心概念。
#x1f4da; 本周知识点回顾
在开始解题之前#xff0c;让我们回顾 Week…本文记录 CS50x Week 2 Problem Set 的解题过程。这周的主题是数组Arrays和字符串Strings我们将通过四道题目深入理解这些核心概念。 本周知识点回顾在开始解题之前让我们回顾 Week 2 的核心概念数组Arrays相同类型数据的连续存储零索引从 0 开始固定大小字符串Strings字符串本质是char数组以\0null terminator结尾string是 CS50 库提供的类型别名实际是char *命令行参数int main(int argc, string argv[])argc参数数量argv[]参数数组常用函数strlen()获取字符串长度isalpha()、isdigit()字符类型判断isupper()、islower()大小写判断toupper()、tolower()大小写转换问题概览问题难度核心概念Scrabble⭐数组映射、字符索引Readability⭐⭐字符串分析、算法实现Caesar⭐⭐命令行参数、字符旋转Substitution⭐⭐⭐密钥验证、字符映射1. Scrabble问题描述在 Scrabble 游戏中玩家通过拼单词得分每个字母有不同的分值字母: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 分值: 1 3 3 2 1 4 2 4 1 8 5 1 3 1 1 3 10 1 1 1 1 4 4 8 4 10任务编写程序让两个玩家输入单词比较得分输出获胜者。思路分析核心问题如何将字母映射到分数关键洞察字母表是有序的可以用数组存储对应分数A B C D E F ... Z ↓ ↓ ↓ ↓ ↓ ↓ ↓ 0 1 2 3 4 5 ... 25 (数组索引) ↓ ↓ ↓ ↓ ↓ ↓ ↓ 1 3 3 2 1 4 ... 10 (分数)字母到索引的转换// 大写字母 A - A 0 (索引0) B - A 1 (索引1) Z - A 25 (索引25) // 小写字母 a - a 0 (索引0) b - a 1 (索引1) z - a 25 (索引25)算法流程1. 定义分数数组 2. 提示两个玩家输入单词 3. 对每个单词计算分数 a. 遍历每个字符 b. 如果是字母转换为索引 c. 累加对应分数 4. 比较分数输出结果代码实现#include cs50.h #include string.h #include ctype.h #include stdio.h // 分数数组索引对应字母表位置 int POINTS[] {1, 3, 3, 2, 1, 4, 2, 4, 1, 8, 5, 1, 3, 1, 1, 3, 10, 1, 1, 1, 1, 4, 4, 8, 4, 10}; // 函数原型 int compute_score(string); int main(void) { // 提示玩家输入单词 string word1 get_string(Player 1: ); string word2 get_string(Player 2: ); // 计算分数 int score1 compute_score(word1); int score2 compute_score(word2); // 比较并输出结果 if (score1 score2) { printf(Player 1 wins!\n); } else if (score1 score2) { printf(Player 2 wins!\n); } else { printf(Tie!\n); } } /* * 计算单词的 Scrabble 分数 * 忽略非字母字符大小写不敏感 */ int compute_score(string word) { int score 0; for (int i 0, n strlen(word); i n; i) { if (isupper(word[i])) { // 大写字母A-A0, B-A1, ... score POINTS[word[i] - A]; } else if (islower(word[i])) { // 小写字母a-a0, b-a1, ... score POINTS[word[i] - a]; } // 非字母字符不计分 } return score; }代码详解1. 为什么用全局数组int POINTS[] {1, 3, 3, 2, ...};优点数据只需定义一次所有函数都能访问不需要作为参数传递注意全局变量通常用大写命名2. 大小写处理技巧if (isupper(word[i])) score POINTS[word[i] - A]; else if (islower(word[i])) score POINTS[word[i] - a];为什么这样写A 和 a 的 ASCII 码不同65 vs 97但它们都映射到索引 0通过减去对应的基准字符A 或 a都得到正确的索引3. 示例计算 CODEC: isupper → C - A 2 → POINTS[2] 3 O: isupper → O - A 14 → POINTS[14] 1 D: isupper → D - A 3 → POINTS[3] 2 E: isupper → E - A 4 → POINTS[4] 1 总分 3 1 2 1 7运行示例$ ./scrabble Player 1: Question? Player 2: Question! Tie!$ ./scrabble Player 1: COMPUTER Player 2: science Player 1 wins!计算验证C(3) O(1) M(3) P(3) U(1) T(1) E(1) R(1) 14s(1) c(3) i(1) e(1) n(1) c(3) e(1) 11关键知识点✅数组作为查找表用索引快速映射数据✅ASCII 算术字符与整数的转换✅字符分类函数isupper()、islower()✅函数抽象将计算逻辑封装成函数2. Readability问题描述根据Coleman-Liau index计算文本的可读性等级适合几年级学生阅读。Coleman-Liau 公式index 0.0588 × L - 0.296 × S - 15.8其中L 每 100 个单词的平均字母数S 每 100 个单词的平均句子数输出规则等级 1输出Before Grade 1等级 ≥ 16输出Grade 16其他输出Grade XX 四舍五入到整数思路分析第一步理解统计规则字母Lettersa-z 和 A-Z单词Words由空格分隔的字符序列句子Sentences以.!?结尾第二步公式转换已知letters, words, sentences 需要计算L 和 S L (letters / words) × 100 S (sentences / words) × 100 index 0.0588 × L - 0.296 × S - 15.8第三步示例计算文本Congratulations! Today is your day.统计字母26 个单词5 个Congratulations, Today, is, your, day句子2 个! 和 . 各结尾一个计算L (26 / 5) × 100 520 S (2 / 5) × 100 40 index 0.0588 × 520 - 0.296 × 40 - 15.8 30.576 - 11.84 - 15.8 2.936 ≈ 3 (四舍五入) 输出Grade 3难点分解难点 1统计字母数int count_letters(string text) { int count 0; for (int i 0, n strlen(text); i n; i) { if (isalpha(text[i])) // 只计算字母 { count; } } return count; }说明isalpha()判断是否为字母a-z, A-Z忽略数字、标点、空格难点 2统计单词数这是本题的核心难点错误方法数空格// ❌ 错误方法 int count 1; // 从 1 开始 for (int i 0; i n; i) { if (text[i] ) count; }问题文本开头/结尾有空格会出错连续多个空格会多计数正确方法状态机int count_words(string text) { int count 0; bool in_word false; // 状态标记是否在单词内部 for (int i 0, n strlen(text); i n; i) { if (text[i] ! !in_word) { // 进入新单词 count; in_word true; } else if (text[i] ) { // 离开单词 in_word false; } } return count; }工作原理以文本Hello world为例注意两个空格位置 字符 in_word 动作 count 0 H false 进入单词 1 1 e true 在单词内 1 2 l true 在单词内 1 3 l true 在单词内 1 4 o true 在单词内 1 5 true 离开单词(false) 1 6 false 仍在单词外 1 7 w false 进入单词 2 ...关键点in_word标记防止重复计数可以正确处理作业要求中提到的多个连续空格可以处理开头/结尾的空格难点 3统计句子数int count_sentences(string text) { int count 0; for (int i 0, n strlen(text); i n; i) { if (text[i] . || text[i] ! || text[i] ?) { count; } } return count; }说明遇到句子结束符就计数三个条件用||或连接难点 4浮点数计算// ❌ 错误整数除法 float L letters / words * 100; // 会丢失小数部分 // ✅ 正确强制类型转换 float L (float) letters / (float) words * 100;为什么需要类型转换假设 letters 26, words 5 整数除法26 / 5 5 (丢失小数部分) 浮点除法26.0 / 5.0 5.2 (保留小数) L 5 × 100 500 ❌ L 5.2 × 100 520 ✅代码实现#include cs50.h #include stdio.h #include string.h #include ctype.h #include math.h // 函数原型 int count_letters(string text); int count_words(string text); int count_sentences(string text); int main(void) { // 提示用户输入文本 string text get_string(Text: ); // 统计字母、单词、句子数 int letters count_letters(text); int words count_words(text); int sentences count_sentences(text); // 计算 Coleman-Liau index float L (float) letters / (float) words * 100; float S (float) sentences / (float) words * 100; float index 0.0588 * L - 0.296 * S - 15.8; // 四舍五入 int grade round(index); // 输出结果 if (grade 1) { printf(Before Grade 1\n); } else if (grade 16) { printf(Grade 16\n); } else { printf(Grade %i\n, grade); } } /* * 统计文本中的字母数a-z, A-Z */ int count_letters(string text) { int count 0; for (int i 0, n strlen(text); i n; i) { if (isalpha(text[i])) { count; } } return count; } /* * 统计文本中的单词数由空格分隔 * 使用状态机避免重复计数和处理多余空格 */ int count_words(string text) { int count 0; bool in_word false; for (int i 0, n strlen(text); i n; i) { if (text[i] ! !in_word) { count; // 进入新单词 in_word true; } else if (text[i] ) { in_word false; // 离开单词 } } return count; } /* * 统计文本中的句子数以 . ! ? 结尾 */ int count_sentences(string text) { int count 0; for (int i 0, n strlen(text); i n; i) { if (text[i] . || text[i] ! || text[i] ?) { count; } } return count; }运行示例示例 1简单文本$ ./readability Text: One fish. Two fish. Red fish. Blue fish. Before Grade 1验证字母36单词8句子4L (36/8) × 100 450 S (4/8) × 100 50 index 0.0588 × 450 - 0.296 × 50 - 15.8 26.46 - 14.8 - 15.8 -4.14 ≈ -4示例 2《哈利·波特》段落$ ./readability Text: Harry Potter was a highly unusual boy in many ways. For one thing, he hated the summer holidays more than any other time of year. For another, he really wanted to do his homework, but was forced to do it in secret, in the dead of the night. And he also happened to be a wizard. Grade 5示例 3高级文本$ ./readability Text: As the average number of letters and words per sentence increases, the Coleman-Liau index gives the text a higher reading level. If you were to take this paragraph, for instance, which has longer words and sentences than either of the prior two examples, the formula would give the text an twelfth-grade reading level. Grade 12关键知识点✅字符串遍历逐字符分析文本✅状态机用布尔标记追踪状态✅字符分类isalpha()等函数✅浮点数运算类型转换避免精度丢失✅数学函数round()四舍五入3. Caesar问题描述实现凯撒密码Caesar Cipher一种古老的加密方法将每个字母按字母表顺序移动固定位数。加密示例密钥k 1明文 H E L L O ↓ ↓ ↓ ↓ ↓ 密文 I F M M PH → I移动 1 位E → FL → MO → P加密公式ci (pi k) % 26其中pi明文字符在字母表中的位置A0, B1, ..., Z25k密钥移动位数ci密文字符的位置% 26模运算确保循环Z 之后回到 A需求分析程序要求命令行参数接受一个非负整数作为密钥参数必须是纯数字加密规则只加密字母保持大小写非字母字符保持不变循环处理Z 1 A即使 k 26 也要正确处理程序流程1. 检查命令行参数数量必须是 1 个 2. 验证参数是否全为数字 3. 将参数转换为整数 4. 提示用户输入明文 5. 逐字符加密 6. 输出密文核心算法字符旋转这是本题的关键难点示例 1基本加密加密大写字母H密钥k 1步骤 1转换为位置0-25 position H - A 72 - 65 7 步骤 2加上密钥并取模 new_position (7 1) % 26 8 步骤 3转回字符 result A 8 65 8 73 I 结果H → I ✓示例 2循环情况加密Z密钥k 1步骤 1 position Z - A 25 步骤 2关键的模运算 new_position (25 1) % 26 26 % 26 0 ← 循环回到开头 步骤 3 result A 0 A 结果Z → A ✓示例 3大密钥加密A密钥k 27position 0 new_position (0 27) % 26 1 result A 1 B 说明k27 等同于 k1 (27 % 26 1)为什么要用 % 26模运算实现字母表的循环位置: 0 1 2 ... 24 25 | 26 27 28 ... 字母: A B C ... Y Z | A B C ... ↑ 循环回到开头 (25 1) % 26 0 → Z 1 A (25 2) % 26 1 → Z 2 B (25 27) % 26 0 → Z 27 A代码实现#include cs50.h #include stdio.h #include string.h #include ctype.h #include stdlib.h // 函数原型 bool only_digits(string s); char rotate(char c, int n); int main(int argc, string argv[]) { // 检查 1参数数量必须是 2程序名 密钥 if (argc ! 2) { printf(Usage: ./caesar key\n); return 1; } // 检查 2密钥必须是纯数字 if (!only_digits(argv[1])) { printf(Usage: ./caesar key\n); return 1; } // 将字符串转换为整数 int key atoi(argv[1]); // 提示用户输入明文 string plaintext get_string(plaintext: ); // 加密并输出 printf(ciphertext: ); for (int i 0, n strlen(plaintext); i n; i) { char encrypted rotate(plaintext[i], key); printf(%c, encrypted); } printf(\n); return 0; } /* * 检查字符串是否只包含数字 */ bool only_digits(string s) { for (int i 0, n strlen(s); i n; i) { if (!isdigit(s[i])) { return false; } } return true; } /* * 使用凯撒密码旋转字符 * 保持大小写非字母字符不变 */ char rotate(char c, int n) { if (isupper(c)) { // 大写字母 int position (c - A n) % 26; return A position; } else if (islower(c)) { // 小写字母 int position (c - a n) % 26; return a position; } else { // 非字母字符保持不变 return c; } }代码详解1. 命令行参数处理int main(int argc, string argv[])运行./caesar 13时argc 2argv[0] ./caesarargv[1] 132. only_digits 函数bool only_digits(string s) { for (int i 0, n strlen(s); i n; i) { if (!isdigit(s[i])) // 检查是否为 0-9 { return false; } } return true; }测试123→true12a3→false-5→false负号不是数字3. rotate 函数的精妙之处// 为什么分别处理大小写 if (isupper(c)) return A ((c - A n) % 26); else if (islower(c)) return a ((c - a n) % 26);原因大小写字母的 ASCII 码不连续ASCII: A 65, B 66, ..., Z 90 a 97, b 98, ..., z 122 ↑ 中间有 6 个其他字符所以必须大写字母以A为基准小写字母以a为基准运行示例示例 1基本加密$ ./caesar 1 plaintext: HELLO ciphertext: IFMMP过程H (7) 1 8 → IE (4) 1 5 → FL (11) 1 12 → MO (14) 1 15 → P示例 2保持大小写和标点$ ./caesar 13 plaintext: Hello, World! ciphertext: Uryyb, Jbeyq!说明H→U大写保持大写e→r小写保持小写,→,标点不变→ 空格不变示例 3循环$ ./caesar 1 plaintext: XYZ ciphertext: YZA验证X (23) 1 24 → YY (24) 1 25 → ZZ (25) 1 26 % 26 0 → A ✓示例 4大密钥$ ./caesar 27 plaintext: ABC ciphertext: BCD说明27 % 26 1效果等同于密钥为 1示例 5错误处理$ ./caesar Usage: ./caesar key$ ./caesar abc Usage: ./caesar key$ ./caesar 1 2 Usage: ./caesar key常见错误错误 1忘记模运算// ❌ 错误 return A (c - A n); // Z 1 [ASCII 91 // ✅ 正确 return A ((c - A n) % 26);错误 2大小写处理不当// ❌ 错误对所有字符使用同一基准 return A ((c - A n) % 26); // 小写字母会出错 // ✅ 正确分别处理 if (isupper(c)) return A ((c - A n) % 26); else if (islower(c)) return a ((c - a n) % 26);错误 3不检查参数// ❌ 错误直接使用 argv[1] int key atoi(argv[1]); // 如果没有参数会崩溃 // ✅ 正确先检查 if (argc ! 2) { printf(Usage: ./caesar key\n); return 1; }关键知识点✅命令行参数argc和argv[]✅字符串验证isdigit()、atoi()✅模运算实现循环% 26✅ASCII 运算字符与整数的转换✅字符判断isupper()、islower()4. Substitution问题描述实现替换密码Substitution Cipher比凯撒密码更复杂使用 26 个字母的密钥将字母表中的每个字母映射到密钥中对应位置的字母。加密原理字母表: A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 密钥: N Q X P O M A F T R H L Z G E C Y J I U W S K D V B映射关系A → N字母表第 1 个 → 密钥第 1 个B → Q字母表第 2 个 → 密钥第 2 个C → X...加密示例加密HELLOH (索引 7) → 密钥[7] F E (索引 4) → 密钥[4] O L (索引 11) → 密钥[11] L L (索引 11) → 密钥[11] L O (索引 14) → 密钥[14] E HELLO → FOLLE需求分析密钥验证规则密钥必须满足长度恰好 26 个字符字符类型只能包含字母唯一性每个字母只能出现一次大小写不敏感ABC...和abc...效果相同加密规则只加密字母保持大小写非字母字符保持不变核心算法算法流程对于明文中的每个字符: 如果是大写字母: 1. 找到它在字母表中的位置 (0-25) 2. 用这个位置作为索引在密钥中查找对应字母 3. 将结果转换为大写 如果是小写字母: 1. 找到它在字母表中的位置 (0-25) 2. 用这个位置作为索引在密钥中查找对应字母 3. 将结果转换为小写 否则: 保持原样示例演示密钥VCHPRZGJNTLSKFBDQWAXEUYMOI加密 H大写步骤 1找位置 index H - A 7 步骤 2查密钥 key[7] J 步骤 3确保大写 result toupper(J) J 结果H → J加密 h小写步骤 1找位置 index h - a 7 步骤 2查密钥 key[7] J (密钥可能是大写) 步骤 3转换为小写 result tolower(J) j 结果h → j关键难点密钥验证这是本题的核心挑战需要同时检查三个条件。难点 1如何检查唯一性错误方法嵌套循环低效// ❌ O(n²) 时间复杂度 for (int i 0; i 26; i) { for (int j i 1; j 26; j) { if (key[i] key[j]) return false; } }正确方法布尔数组高效// ✅ O(n) 时间复杂度 bool letter_seen[26] {false}; for (int i 0; i 26; i) { char c toupper(key[i]); int index c - A; if (letter_seen[index]) // 已经出现过 return false; letter_seen[index] true; // 标记为已出现 }布尔数组的工作原理数组letter_seen[26] 索引 0 1 2 ... 25 字母 A B C ... Z 值 true/false (是否出现过) 示例处理密钥 ABC... 初始状态 [F F F F F ... F] (全为 false) 遇到 A: index 0 letter_seen[0] 是 false? ✓ → 设置为 true [T F F F F ... F] 遇到 B: index 1 letter_seen[1] 是 false? ✓ → 设置为 true [T T F F F ... F] 如果再遇到 A: index 0 letter_seen[0] 是 false? ✗ (已经是 true) → 返回 false (密钥无效)这种技术叫做标记数组或访问数组非常常用代码实现#include cs50.h #include stdio.h #include string.h #include ctype.h // 函数原型 bool is_valid_key(string key); char substitute(char c, string key); int main(int argc, string argv[]) { // 检查 1参数数量 if (argc ! 2) { printf(Usage: ./substitution key\n); return 1; } // 检查 2密钥有效性 if (!is_valid_key(argv[1])) { printf(Key must contain 26 unique alphabetic characters.\n); return 1; } string key argv[1]; // 提示用户输入 string plaintext get_string(plaintext: ); // 加密并输出 printf(ciphertext: ); for (int i 0, n strlen(plaintext); i n; i) { char encrypted substitute(plaintext[i], key); printf(%c, encrypted); } printf(\n); return 0; } /* * 验证密钥是否有效 * 必须26个字符全是字母无重复 */ bool is_valid_key(string key) { int len strlen(key); // 检查长度 if (len ! 26) { return false; } // 使用布尔数组追踪字母是否出现过 bool letter_seen[26] {false}; for (int i 0; i len; i) { char c toupper(key[i]); // 检查是否为字母 if (!isalpha(c)) { return false; } // 检查是否重复 int index c - A; if (letter_seen[index]) { return false; // 这个字母已经出现过 } letter_seen[index] true; // 标记为已出现 } return true; } /* * 使用替换密码加密字符 * 保持大小写非字母字符不变 */ char substitute(char c, string key) { if (isupper(c)) { // 大写字母 int index c - A; return toupper(key[index]); } else if (islower(c)) { // 小写字母 int index c - a; return tolower(key[index]); } else { // 非字母字符 return c; } }代码详解1. is_valid_key 函数的巧妙设计bool letter_seen[26] {false};一次遍历完成三个检查for (int i 0; i 26; i) { char c toupper(key[i]); // 检查 1是否为字母 if (!isalpha(c)) return false; // 检查 2是否重复 int index c - A; if (letter_seen[index]) return false; // 标记为已出现 letter_seen[index] true; }效率分析时间复杂度O(26) O(1)空间复杂度O(26) O(1)一次遍历完成所有检查2. substitute 函数char substitute(char c, string key) { if (isupper(c)) return toupper(key[c - A]); else if (islower(c)) return tolower(key[c - a]); else return c; }为什么要用 toupper/tolower因为密钥本身大小写不敏感./substitution vchpr... # 小写密钥 ./substitution VCHPR... # 大写密钥 # 两者效果相同但加密后的密文大小写需要与原明文大小写保持一致大写 → 大写H → J小写 → 小写h → j3. 完整加密示例密钥VCHPRZGJNTLSKFBDQWAXEUYMOI明文Hello, World!H (索引 7) → key[7] J → toupper(J) J e (索引 4) → key[4] R → tolower(R) r l (索引 11) → key[11] S → tolower(S) s l (索引 11) → key[11] S → tolower(S) s o (索引 14) → key[14] B → tolower(B) b , → , → W (索引 22) → key[22] Y → toupper(Y) Y o (索引 14) → key[14] B → tolower(B) b r (索引 17) → key[17] W → tolower(W) w l (索引 11) → key[11] S → tolower(S) s d (索引 3) → key[3] P → tolower(P) p ! → ! 结果Jrssb, Ybwsp!运行示例示例 1基本加密$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOI plaintext: HELLO ciphertext: JRSSB示例 2保持大小写$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOI plaintext: Hello, World! ciphertext: Jrssb, Ybwsp!示例 3密钥大小写混合$ ./substitution VchprZGJNtlskfbdQWAXeuymoi plaintext: HELLO ciphertext: JRSSB结果相同密钥大小写不影响示例 4错误情况密钥太短$ ./substitution ABC Key must contain 26 unique alphabetic characters.密钥包含非字母$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYM01 Key must contain 26 unique alphabetic characters.密钥有重复字母$ ./substitution VCHPRZGJNTLSKFBDQWAXEUYMOII Key must contain 26 unique alphabetic characters.最后有两个 I无参数$ ./substitution Usage: ./substitution key对比凯撒密码 vs 替换密码特性凯撒密码替换密码密钥一个数字0-2526 个字母的排列可能性26 种26! ≈ 4×10²⁶ 种安全性极低可暴力破解较高加密逻辑固定位移任意映射示例k1: A→B, B→C密钥定义: A→V, B→C有趣的事实凯撒密码是替换密码的特殊情况凯撒密码 k1 的密钥 BCDEFGHIJKLMNOPQRSTUVWXYZA关键知识点✅命令行参数验证多重条件检查✅布尔数组技巧高效追踪字母出现✅字符映射使用索引实现替换✅大小写处理toupper()、tolower()✅字符判断isalpha()、isupper()、islower() Problem Set 2 总结知识点对比问题核心数据结构核心算法难度亮点Scrabble数组字符映射数组作为查找表Readability字符串文本分析状态机、浮点运算Caesar字符串字符旋转模运算、大小写处理Substitution字符串、布尔数组字符映射密钥验证、标记数组重要技术总结1. 数组技巧查找表用数组索引快速映射数据标记数组用布尔数组追踪状态2. 字符串处理字符分类isalpha(),isdigit(),isupper(),islower()字符转换toupper(),tolower(),atoi()字符串遍历for (int i 0, n strlen(s); i n; i)3. ASCII 运算// 字母到索引 A - A 0, B - A 1, ..., Z - A 25 a - a 0, b - a 1, ..., z - a 25 // 索引到字母 A 0 A, A 1 B, ..., A 25 Z a 0 a, a 1 b, ..., a 25 z4. 模运算(position key) % 26 // 实现字母表循环5. 状态机bool in_word false; // 用状态标记追踪复杂逻辑6. 类型转换(float) a / (float) b // 避免整数除法丢失精度编程技巧✅ 好的实践函数抽象将复杂逻辑封装成函数参数验证检查输入有效性清晰命名函数名表达意图注释说明解释关键算法边界测试测试极端情况❌ 常见错误整数除法忘记类型转换数组越界不检查长度忘记模运算字符旋转不循环大小写混淆A 和 a 的基准不同参数检查直接使用 argv 而不验证学习建议对于初学者理解优先先理解算法再写代码分步实现逐个函数实现和测试画图辅助画出数组、字符映射关系测试驱动先写测试用例再写代码调试技巧打印中间值验证逻辑下一步完成 Problem Set 2 后你已经掌握了✅ 数组的使用和技巧✅ 字符串的处理方法✅ 命令行参数的处理✅ 基本的密码学概念Week 3 将学习算法Algorithms排序、搜索、递归等敬请期待参考资源CS50 Week 2 主页CS50 Week 2 NotesCS50 Manual PagesASCII TableHappy Coding!