
刷到 PAT 甲级 1035 这道题的时候你大概率已经过了语言基础关。Password密码这个 20 分的题属于甲级里少见的纯送分题——没有复杂数据结构没有绕弯子的“思维难度”甚至样例都不算长。但恰恰是这种题成了不少人的失分重灾区样例一次过交上去却拿不到满分回头一看挂在There is 1 account...那行英文上。这道题的核心很简单把一批账号的密码里容易混淆的四个字符替换掉统计并输出被修改的账号。整体就是一层字符串模拟一点算法含量都没有。它真正考验的是三件事读题的细心程度、字符映射的准确性、以及英文输出的单复数处理。把这三点讲透你不仅能拿满这道题的分还能顺手避开 PAT 字符串类题目里一大半的坑。1. 这道 20 分题到底在考什么1.1 题目背景一个很现实的密码检查场景题面本身是一个完整的业务场景系统里有一堆用户账号每个账号有用户名和密码。管理员决定对密码做一次统一“体检”凡是在视觉上容易看错的字符全部替换成不容易看错的形式。具体的替换规则是原字符含义替换为含义1数字一艾特符号0数字零%百分号l小写字母 LL大写字母 LO大写字母 Oo小写字母 o这套映射是整道题的灵魂。你盯着它看十秒钟就能发现规律数字 1 和字母 l小写 L、数字 0 和字母 O大写 O在不少等宽字体和手写场景下几乎长得一模一样所以系统把它们统一换掉。这个思路在真实世界里完全存在比如验证码识别、OCR 后的文本规范化、甚至一些系统在导入用户数据前做的“脏字符清洗”。但考试不会考你业务怎么落地它只是借这个场景包装最简单的字符串处理。题目输入的 N 行里每行是用户名 密码两串之间用普通空格分开。你需要逐项检查密码遇到上述四个危险字符就替换统计有多少账号发生了修改。这里有个容易忽略的规则如果某个账号的密码完全正常它就不出现在输出里连账号信息都不需要打印。1.2 表面是字符串题实际是“细心题”绝大多数人不至于看不懂题目真正丢分的地方是细节。我把这类题称为“细心题”因为它的全部得分点都埋在看似平淡的描述里。第一不是所有账号都要输出。只有密码发生过替换的账号才需要在结果里列出。你可能觉得这是废话但真到写代码的时候很多人会顺手把所有账号都输出一遍或者只输出密码不输出修改后的账号样例里的数字对不上才反应过来。第二第一行要先输出被修改的账号数量。如果修改了 3 个账号第一行就是一个孤零零的3接下来才是 3 行账号信息。如果没有任何账号需要修改则完全不输出数量而是输出一句固定的英文说明并且这句英文要随账号总数变化。第三也是最阴险的一点没有修改时输出语法的单复数要跟着 N 变。N 等于 1 时输出There is 1 account and no account is modifiedN 大于 1 时输出There are N accounts and no account is modified。“is/are”“account/accounts”只要错一个这个测试点就扣分。这三条规则如果考试时没记住样例一过就很容易带偏。我的建议是读题时把输出格式的原文抄在草稿纸上特别是那句英文逐词抄别用记忆代替。2. 解题思路模拟流程怎么设计才不会乱2.1 先定数据容器再谈遍历这种 20 分的模拟题不需要任何高深的数据结构。你只需要一个能按输入顺序保存“被修改账号”的容器外加一个计数器。我习惯用 C 的vectorpairstring, stringfirst存用户名second存修改后的密码。为什么不直接只存密码因为输出时需要用户名 新密码两个字段成对存起来最不容易错。计数变量单独用一个int modifiedCount维护别指望拿容器 size 去推后面你就知道为什么了。2.2 遍历修改的两种写法怎么选处理每个账号时核心是一个“遍历密码字符并判断”的循环。有两种常见写法写法 A直接在原字符串上改。用引用遍历遇到危险字符就用映射后的字符覆盖当前位置同时把修改标记置为 true。写法 B复制一份新字符串遍历原字符串往新串里拼接或者干脆在临时副本上改改完了再把新密码赋回去。两种都能过但我强烈建议用写法 A。原因很直接题目已经指定了四个危险字符你只需要做四个判断原地修改完全可控。写法 B 多一次拷贝逻辑上没有多带来任何安全感反而增加了“到底哪份字符串是新的”这种认知负担。不过写法 A 有一个隐蔽的讲究四个判断要用if-else if串起来还是一个字符一个独立if因为一个字符不可能同时等于1、0、l、O中的两个所以else if和独立if结果等价。但用else if的代码更干净可读性也更好。真正的关键点是修改标记flag必须在每次替换时置为 true而且只需要置一次。我见过有人为了“省事”在循环里写了这样的逻辑改一个字符就开始统计数量结果一个密码里有三个危险字符时账号被统计了三次。这就是没把“账号是否被修改”和“密码中有几个危险字符”区分开。2.3 伪代码先行逻辑根本不乱动手敲键盘之前先在脑子里过一遍完整流程。我给一个通用伪代码for 每个账号: flag false for 密码中的每个字符 c: if c 1: c , flag true else if c 0: c %, flag true else if c l: c L, flag true else if c O: c o, flag true if flag true: modifiedCount 把 用户名 和 修改后的密码 存进结果容器 if modifiedCount 0: 输出 modifiedCount 输出容器里的每一行 else: if N 1: 输出 There is 1 account and no account is modified else: 输出 There are N accounts and no account is modified到这里整道题的架构就已经完整了。后面无非是把伪代码翻译成具体语言再把那些容易踩的输出细节磨平。3. 参考实现C 解法与每行代码的 WHY3.1 完整代码我直接用 C 写一版常规模板代码量不大但每行都有它的作用#include iostream #include vector #include string using namespace std; int main() { int n; cin n; vectorpairstring, string changed; int modifiedCount 0; for (int i 0; i n; i) { string username, password; cin username password; bool flag false; for (char c : password) { if (c 1) { c ; flag true; } else if (c 0) { c %; flag true; } else if (c l) { c L; flag true; } else if (c O) { c o; flag true; } } if (flag) { modifiedCount; changed.push_back({username, password}); } } if (modifiedCount 0) { cout modifiedCount endl; for (auto p : changed) { cout p.first p.second endl; } } else { if (n 1) { cout There is 1 account and no account is modified endl; } else { cout There are n accounts and no account is modified endl; } } return 0; }这段代码的得分点全部藏在细节里。for (char c : password)的引用符号决定了修改能否写回原字符串少了它这个字符循环就变成了“只读检查”输出时密码还是原样测试点全挂。c被修改后flag立刻被置为 true这样即使一个密码里有多个危险字符这个账号也只会被统计一次。push_back({username, password})存入的是修改后的密码因为password这个对象已经被引用遍历同步更新了。3.2 为什么用 cin 而不是 scanf很多从 C 语言转过来的同学会习惯性用scanf(%s %s, ...)这当然能做但用 C 的cin读string会自然得多尤其后面要做char c这种遍历。如果你担心cin太慢可以在main开头加一行ios::sync_with_stdio(false);和cin.tie(nullptr);性能就完全够用。这个题的数据量很小就算不加也绝对能过但加上可以养成习惯。这里有一个容易踩的输入细节题目里用户名和密码都是连续字符串不含空格。所以cin username password就能正确读取。千万不要自己去getline读整行然后手动切分那是自己给自己增加难度。万一之后遇到带空格的题目再单独处理即可。3.3 如果你想用 C 语言实现如果你打算用纯 C 写也一样核心思路没有变化。定义一个char username[100][20]一个char password[100][20]循环里逐字符判断并覆盖用一个数组记录哪些账号需要输出。唯一的麻烦是strcpy和二维数组的下标管理比 C 的vector稍微绕一点。PAT 是允许 C 的我没有理由放弃string的便利硬去搞 C 字符串操作。4. 从读题到 AC全真样例推演4.1 手推标准样例我构造一组比较有代表性的数据把“修改账号 不修改账号 多字符修改”集中在一个样例里输入4 user01 A1b2C3 user02 AbCdeF user03 lOgin user04 000111逐行推演user01的密码A1b2C3里有字符1替换成新密码是Ab2C3。修改了要输出。user02的密码AbCdeF里没有任何危险字符不修改不输出。user03的密码lOgin里同时有l和O分别替换成L和o新密码是Login。修改了要输出。user04的密码000111里全是0和1替换后变成%%%。修改了要输出。所以输出应该是3 user01 Ab2C3 user03 Login user04 %%%这个样例覆盖了三种典型情况单字符修改、大小写字母混合修改、多个字符连续修改。写代码的时候用这个样例验证modifiedCount是 3vector里有 3 行内容全部对上。4.2 没有修改时的英文输出雷区如果所有账号都“体检合格”上面的分支就不成立进入英文输出分支。这里有两个几乎必考的边界输入1 admin Admin123输出There is 1 account and no account is modified输入2 admin Admin123 root Root!23输出There are 2 accounts and no account is modified第一行因为N 1主语是单数account谓语用is。第二行因为N 1主语是复数accounts谓语用are。就这么点差别能卡掉一大批人。还有几个容易被忽略的小格式问题整句英文末尾没有句号modified的拼写是 m-o-d-i-f-i-e-d不是 modified 漏个字母There is的 T 大写中间每个单词之间只有一个空格别多个空格。输出时我建议直接整行复制题面英文不要敲一遍后凭记忆补单词。有人会问如果N 0怎么样题目约定了 N 是账号个数正常不会给你 0。真遇到这种用例按N 1的输出反而会输出There are 0 accounts...但 PAT 数据集不会这么无聊不用专门处理。5. 我踩过的坑与排查速查表5.1 坑一号映射关系记混l替换成LO替换成o。这句话看起来简单实际做题时不少人把大小写方向搞反尤其看见O是个大写字母下意识认为它该替换成更醒目的0结果输出了一堆数字 0 进去。之所以O - o是因为大写 O 容易和数字 0 混淆统一成小写 o 后就不容易看错。同理l小写 L 容易和数字 1 混淆替换成大写的L更醒目。我的建议是把你看到的映射表原封不动抄到自己草稿纸上字符下面标清楚“这是数字”“这是小写 L”“这是大写 O”。考场上最怕的不是不会而是看着屏幕想当然。5.2 坑二号统计数量和输出内容对不上代码里的modifiedCount和vector是配套的。有人只写了计数器最后输出时没有打印存下来的账号有人把没修改的账号也塞进了vector还有人把if (flag)写成了if (password[i] ! password[i])这种永远不成立的条件样例一运行输出是 0才知道大事不妙。排查思路很简单先自己构造一个只有 1 个危险字符的账号跑通后再构造一个全是危险字符的账号确认计数不会变成“字符个数”。只要这两个 case 都对统计逻辑基本稳了。5.3 坑三号英文输出的单复数和空格这一坑是本题最容易扣分的地方。题面给出的英文输出有两种差的不是一两个字母而是整个句子的语法结构。我考完总结了一个自检顺序第一步看N是不是 1。第二步决定用is还是are。第三步决定用account还是accounts。第四步检查modified拼写。第五步数一遍空格数量确认没有多余空格末尾没有句号。这套流程听起来很笨但如果你参加过机考就会发现人紧张的时候最容易在英文输出上犯低级错误机械地按顺序检查反而最可靠。5.4 坑四号读取输入时把换行符卷进来用cin读不会遇到这个问题因为会跳过空白字符。但如果你用了getline上一行读完 N 之后残留的换行符会直接进到第一行密码里导致整道题错得莫名其妙。解决办法是读完整数后忽略掉getline残留的换行或者干脆全用cin 处理不要去碰getline。我把常见错误整理成一个速查表方便你自测错误类型典型表现排查方法引用遍历漏写输出时密码没变化检查 for 循环处是否用char c统计重复一个账号输出多遍确认flag只影响一次 push_back映射方向反l变I、O变0对照题面映射表逐项核对英文单复数错N1 时输出There is独立写 if/else 分支输出多余账号没修改的账号也打印确认 push_back 在 if(flag) 内末尾多余换行格式错但内容对每行输出用 endl 自然结束你可能觉得这些问题很“蠢”但考试扣分从来不看你聪明不聪明只看输出对不对。6. 从 1035 看 PAT 甲级备考策略6.1 20分题必须拿满失分点往往不在算法PAT 甲级的分数结构很明确20 分的题属于基础题是整场考试的“底盘”。你后面的大题写得再漂亮前面送分题被扣个 4、5 分整体分数就会很难看。我发现很多人备考有个误区死磕难题轻视简单题。结果就是难题没啃下来送分题又因为粗心扣分。以这道 Password 为例它根本不需要你掌握任何高级算法但它的得分点确确实实藏在输出规范和边界测试里。对待这种题我建议做完样例后不要急着提交先自制两个极端小数据一个所有密码都不需要修改N 分别取 1 和 2 各测一次一个密码里全是危险字符测一次。把这三种情况跑通再提交基本就是满分。6.2 这道题还能给你什么启发字符串替换看起来简单但它是很多真实系统里常见的预处理步骤。你在做日志分析、数据清洗时经常需要对字段做“标准化”把各种长得差不多的字符统一成一种。这道题的框架完全可以平移过去遍历集合、按规则替换、记录受影响项、输出总结报告。从 PAT 的整个知识体系看1035 属于典型的“模拟 字符串基础”它的难度和甲级后半段那些图论、二叉搜索树、动态规划题完全不在一个量级。但正因为简单它才更适合用来练手感。我刷题的时候特别喜欢用这类题来检查自己的代码习惯有没有用引用遍历、有没有正确处理英文输出、有没有在边界条件上偷懒。如果让我给一个备考顺序的建议先把 20 分题全部刷一遍保证每道都能一遍 AC再往上攻 25 分题和 30 分题。原因是低级错误会在你处理复杂逻辑时被无限放大而简单题恰恰是暴露低级错误的试金石。最后分享一个我个人的实测经验。这道题我第一次做的时候逻辑写得飞快样例一次过心里美滋滋交上去结果扣了 4 分回来看就是N 1时输出写成了There is。后来我给自己立了一条规矩凡是输出英文句子的题提交前必须逐词检查单复数和空格不检查不提交。这条规矩后来救了我好几道 20 分题。编程比赛的分数往往就差在这种不起眼的地方被扣一次不丢人记住这个教训才是真的赚到了。