ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

华为机考统计字符题全解析:从遍历到避坑一次讲透

华为机考统计字符题全解析:从遍历到避坑一次讲透 前阵子帮几位准备华为机考的朋友看代码发现一道高频送分题反复被提起——统计字符。这道题在华为校园招聘、OD机考、甚至部分社招机考里都出现过题目本身很简单给你一行字符串统计里面的英文字母、空格、数字和其他字符的个数。但就是这种看似简单的题反而能刷掉一批人。原因不在于算法难而在于细节多、输入输出阴很多人思路秒出代码也秒写一提交就是0分。我把这道题从题目拆解、多种解法、完整实战再到常见坑位一次性讲透。不管你是刚开始刷题的新手还是准备冲刺高分的老手这篇文章都能让你在考场上少踩几个坑。1. 题目拆解统计字符到底在考什么1.1 原题描述与输入输出约定先还原一下这道题的典型描述输入一行字符串可能包含空格请统计其中英文字母、空格、数字和其他字符的个数按顺序输出四个整数。比如输入Hello World 123!输出应该是英文字母HelloWorld共10个空格2个数字123共3个其他字符!共1个所以输出为10 2 3 1。这个题目逻辑上没有任何难度拆开就是一个字符遍历加分类计数的问题。但它的陷阱藏在“可能包含空格”和“如何从控制台正确读取带空格的整行”这两点上。1.2 四个隐藏考点别以为只是数个数第一字符分类的边界定义。题目说的“英文字母”指的是a-z和A-Z不包含中文字符、不包含带重音符号的拉丁字母。注意这一点很重要如果你判断时用Character.isLetter()那中文字符也会被分到“字母”类里大概率直接错。第二空格的识别。普通的半角空格是 但输入里如果混入全角空格\u3000中文输入法打出来的空格很多人的判断直接翻车。虽然华为机考的绝大多数测试用例都是半角空格但严谨的写法应该明确判断c 或用Character.isWhitespace(c)。第三输入读取方式。这是机考翻车重灾区。Scanner.next()碰到空格就停了只能读到一个单词要用Scanner.nextLine()才能读到整行。很多人卡在这一步后面全白写。第四输出格式。四个数字之间用空格分隔末尾不能有多余空格。有的同学用print拼接时不小心多打了一个空格或者少打了换行都会扣分。2. 三种主流解法的思路对比2.1 遍历判断法最稳妥的常规解法这是最推荐的做法也是我在考场上唯一会用的方案。思路一句话把字符串转成字符数组逐个判断分类四种计数器累加。import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc new Scanner(System.in); String line sc.nextLine(); int letter 0; int space 0; int digit 0; int other 0; for (int i 0; i line.length(); i) { char c line.charAt(i); if ((c a c z) || (c A c Z)) { letter; } else if (c 0 c 9) { digit; } else if (c ) { space; } else { other; } } System.out.println(letter space digit other); } }这里我用的是 ASCII 码区间判断而不是封装方法。原因很简单Character.isLetter()会把中文当字母Character.isDigit()会把全角数字当数字在一个对“字符类型”有严格定义的基础题里用现成方法反而容易出事。手写区间判断明确、可控、绝对不会出歧义。2.2 正则表达式代码量最少但踩坑最多用正则做统计确实看起来很飘逸Python 写起来尤其短import re s input() letters len(re.findall(r[a-zA-Z], s)) spaces s.count( ) digits len(re.findall(r\d, s)) others len(s) - letters - spaces - digits print(letters, spaces, digits, others)但我不建议机考时用正则。原因有两个一是re.findall会额外创建列表对象在数据量大的时候性能比遍历差二是正则表达式写错了不好排查比如\d默认匹配 Unicode 数字全角数字也能匹配上这时候数字计数就和题目要求不一致了。不过如果只是快速验证思路或者平时做业务开发里的字符串统计正则这套代码完全够用。2.3 流式与函数式写法赏心悦目但别炫技Java 8 的 Stream 写法很优雅long letters line.chars().filter(c - (c a c z) || (c A c Z)).count(); long spaces line.chars().filter(c - c ).count(); long digits line.chars().filter(c - c 0 c 9).count(); long others line.length() - letters - spaces - digits;这段代码在本地跑完全没问题看起来也专业但华为机考要求写一个完整的可运行类Stream 的chars()返回的是IntStream如果要转回char还得费一番手脚反而增加了出错概率。机考只有一次提交机会花里胡哨不如稳扎稳打所以我强烈建议就写最普通的 for 循环。3. 完整实战从读题到 AC 的整个流程3.1 环境准备与输入处理细节华为机考一般在牛客网或者华为自研的 OJ 系统上做支持 C/C、Java、Python、Go 等主流语言。以 Java 为例提交的代码必须包含一个public class Main主方法写在main里。类名错了直接编译不通过这个细节很多人第一次考都不知道。输入这块题目说“输入一行字符串”就意味着可能有空格。一定用nextLine()不要用next()或nextInt()。有的同学习惯在读取整数后再用nextLine()这时如果上一行残留了换行符就会读到一个空串导致所有统计结果都是 0。这个坑极其经典。如果你的机考环境里题目是“多组输入、每组一行”那就要用hasNextLine()循环处理while (sc.hasNextLine()) { String line sc.nextLine(); // 统计并输出 }但大多数统计字符的题只有一组输入循环反而会卡在等待输入上导致超时。拿到题目先看输入描述是“一行输入”就只读一次。3.2 完整代码与逐步讲解我给出一个带注释的完整版本方便你直接对照理解import java.util.Scanner; public class Main { public static void main(String[] args) { Scanner sc new Scanner(System.in); // 题目保证输入一行字符串可能包含空格 String line sc.nextLine(); int letterCount 0; int spaceCount 0; int digitCount 0; int otherCount 0; // 遍历字符串中的每一个字符 for (int i 0; i line.length(); i) { char c line.charAt(i); // 判断英文字母只统计 a-z / A-Z if ((c a c z) || (c A c Z)) { letterCount; } // 判断数字只统计 0-9 else if (c 0 c 9) { digitCount; } // 判断空格这里是半角空格 else if (c ) { spaceCount; } // 其余归为其他字符 else { otherCount; } } // 按要求输出注意空格分隔 System.out.println(letterCount spaceCount digitCount otherCount); } }这段代码我实测在多个 OJ 系统上都能直接 AC不依赖任何第三方库也不涉及复杂 API。核心逻辑就是那三个else if顺序不重要因为每个分支的条件是互斥的。3.3 用样例验证输出拿Hello World 123!来手动跑一遍H e l l o5个字母空格1个W o r l d5个字母空格1个1 2 33个数字!1个其他字符统计结果10 2 3 1。程序输出10 2 3 1与控制台期望一致。再拿一个极端样例 两个空格字母0空格2数字0其他0输出0 2 0 0也正确。边界样例能跑通这题基本就稳了。4. 必踩的坑统计字符的边界与陷阱4.1 中文字符和全角符号的分类问题这是我在辅导时遇到最多的问题。有人输入你好 abc 123期望把“你”“好”算到其他字符里结果因为用了Character.isLetter()把中文字符也算成字母导致 letter 多了 2。还有全角数位被isDigit()识别成数字导致计数偏差。所以再次强调机考环境的测试用例就算全都用 ASCII 字符你自己也不能依赖isLetter()。手写区间判断一劳永逸既不用记 API 行为也不会有歧义。4.2 换行符 \r\n 的处理Windows 环境下输入行末尾可能携带\r回车符。如果你用nextLine()读取Java 会自动过滤掉行末的换行和回车通常没问题。但如果你用BufferedReader.readLine()手动读取某些平台上\r会残留在字符串里然后被算成“其他字符”导致结果多 1 或 2。解决办法是在读完字符串后做一个 trim 吗不行trim()会去掉行首行尾的空格题目里的空格是有效的不能删。正确做法是只去除末尾的\rif (line.endsWith(\r)) { line line.substring(0, line.length() - 1); }或者干脆用replace(\r, )。不过华为机考通常用 Java 的 Scanner 就够这个坑主要在 C 的getline场景里更明显。4.3 输出格式和末尾空格我很理解考试时紧张的心情但这种细节恰恰是拉开分数的地方。要求输出N N N N那就一个空格分隔、末尾不要空格、最后换行。System.out.println(letter space digit other)这种写法天然满足要求别自己拼字符串时多加少加。4.4 常见问题速查表现象可能原因解决方案读到的字符串只有第一个单词用了next()而不是nextLine()改用sc.nextLine()中文字符被统计成字母用了Character.isLetter()手写c a c z全角数字被统计成数字用了Character.isDigit()手写c 0 c 9其他字符比预期多字符串末尾有\r读取后去掉\r输出格式错误拼接时空格多了或少了用println配合 代码编译失败类名不是Main改成public class Main去掉 package 声明5. 这道题背后的机考出题逻辑与刷题策略5.1 为什么送分题反而能刷人我在帮人复盘时发现统计字符这类题在华为机考里属于一星难度但它检验的是最基础的三件事会不会处理 IO 输入、会不会写分支判断、能不能细心处理边界条件。这三件事恰恰是平时写业务代码时最容易忽略的。题做得多的朋友应该有个体会很多中等难度的题核心思路并不难难的是“听过很多道理依然过不了样例”。原因就是细节失分。华为机考的评分系统通常按通过用例比例给分一个大用例挂了可能直接掉一个档位。5.2 三道高频变体从送分题到拉分题统计字符这题本身不难但它的变体在机考里经常出现我列三个最常见的一是字符频率统计。要求输出出现次数最多的字符及其次数或者按频率排序输出。这类题从简单的计数变成了对 Map 的使用和排序代码量上了一个台阶但核心仍是逐字符处理。二是字符串中子串出现次数。比如统计abc在一行文本里出现了几次主要考察indexOf配合循环查找。很多人会想到split但要注意分隔符在开头结尾时会产生空串需要额外处理。三是按字符类别重新拼接字符串。比如要求先输出所有数字、再输出字母、最后输出其他字符但保持各自顺序。这其实就是在四个数组或列表里做追加最后统一输出。如果你把统计字符这道题吃透写清楚 for 循环、字符判断、StringBuilder再去做这三道变体会发现很多思想是通用的。5.3 刷题节奏建议这道题要不要花时间可能有人觉得统计字符太简单不值得专门准备。我的观点是不必专门刷但必须动手写一遍而且要在真实的 OJ 环境里提交一次。原因很实在机考时对自己的代码有信心很大程度上来自肌肉记忆。你平时写过nextLine()配charAt()的手感到考场上就不会在 Scanner 的坑里卡十分钟。我的建议是每天花 15 分钟把这类一星题翻来覆去写熟练重点不是记住答案而是形成一套稳定的输入输出模板。我自己在练习时会把模板固定下来Scanner sc new Scanner(System.in)、nextLine()读字符串、for循环里charAt取字符、四个计数器累加。不管题目变成统计什么框架都不变。这种“模板化”思维在考场上非常节省脑力你可以把宝贵的注意力留给真正有难度的题。我特别想提醒的是机考之前不要只看题解一定要亲手在 OJ 上提交一遍哪怕是你觉得十拿九稳的题目。因为真正提交后你才会发现读题、编译、运行、比对样例这些流程里藏着数不清的小问题。统计字符这道题是我见过最适合当“上手第一题”的题目用它来熟悉整套机考流程再合适不过。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进