
看到“先飞的笨鸟”这个标题我第一反应是乐了这不就是当年刚开始刷LeetCode的我吗第58题“最后一个单词的长度”在题库里是个不起眼的简单题难度标记为Easy通过率长期在四成上下徘徊。但仔细琢磨会发现这道题一点都不“简单”——它的代码量小、边界条件隐蔽、解法可以从一行写完到双指针优雅收尾还恰好踩中了字符串处理里最典型的几个坑。对于准备面试、想要系统刷题、或者刚开始接触LeetCode的朋友来说把它当成“笨鸟先飞”的第一站真是再合适不过。这道题题干很短给定一个字符串s由若干单词组成单词之间用空格隔开返回最后一个单词的长度。听起来毫无威胁但很多人在笔试现场卡住的恰恰不是算法而是对边界情况的处理。比如末尾可能有多个空格、字符串可能全部是空格、单词里混着不可见字符等等。这篇文章我会把这道题的题目拆解、多种实现方案、复杂度对比、边界测试用例全部过一遍再结合我实际刷题过程中踩过的坑聊聊怎么用一道简单题串起LeetCode的学习路径。适合刚入门的朋友照着一行行敲也适合正在备战面试的读者当作查漏补缺的清单。我会尽量把每一步为什么这么做讲清楚而不是只给答案。1. 先读懂题目最后一个单词到底在考什么1.1 题面拆解与“笨鸟”定位第58题的原题描述是这样的给你一个字符串s由若干单词组成单词前后用一些空格字符隔开请返回字符串中最后一个单词的长度。单词是指仅由字母组成、不包含任何空格字符的最大子字符串。理解这句话的关键在最后一句“仅由字母组成、不包含任何空格字符的最大子字符串”。这意味着题目假设单词里只有字母不会出现数字、标点符号。这个设定其实是一个隐含的简化条件它让我们可以把注意力完全集中在“怎么找到最后一个非空格片段”这件事上。为什么说这道题是“笨鸟先飞”的最佳起点因为它同时具备三个特征第一不需要任何高深的数据结构知识数组和字符串操作就够用第二解法多样从最简单的借口函数到手动遍历都能实现适合不同阶段的人反复练手第三边界条件足够多能提前暴露很多新手在字符串处理上的通病。这三个特征组合在一起让这道题成为一道“低门槛、高收益”的入门题。所谓先飞的笨鸟讲的不是能力差而是通过提前把基础打牢用别人还在看题的时候你已经能秒掉的速度差来建立优势。1.2 为什么第58题适合作为第一只“笨鸟”我在各个刷题群里看到过很多人的第一题选择。有人一上来就挑战动态规划结果被状态转移方程劝退有人直接冲困难题半天提交一次心态崩得飞快。第58题作为一只“笨鸟”的起飞点最大的价值在于它能让你在第一天就体会到“完整解题流程”带来的正反馈读题、设计思路、写代码、跑测试、提交通过整个过程可能只需要十五分钟。这种正反馈对长期刷题特别重要。LeetCode上题目已经超过三千道一天刷三道也得三年多才能刷完没有正反馈的支撑坚持不下去。而第58题恰好能在最短时间内让你确认自己做题的方法论是否成立。另外这道题在面试中出现频率不低因为它可以快速考察候选人对字符串处理和边界条件的敏感度。很多公司的笔试第一题就是类似难度用来筛选基本编码能力。把这道题练到闭着眼睛能写出来绝对不亏。2. 核心实现两种解法的完整拆解2.1 最直观的split解法大多数人第一次看到这道题的第一反应是用split按空格把字符串切成列表然后取最后一个元素。用Python写甚至只需要两行def lengthOfLastWord(s: str) - int: words s.split() return len(words[-1]) if words else 0这个解法能通过是因为Python的split方法有个特性如果不传参数它会按任意连续的空白字符进行分割并且自动忽略首尾的空格。也就是说输入字符串 fly me to the moon 会被直接切成[fly, me, to, the, moon]。连续多个空格不会产生空字符串元素这省去了很多麻烦。但是我会建议你写完之后停下来想一想这个解法的时间复杂度是O(n)可以接受但是空间复杂度也是O(n)因为你生成了一个列表把整个字符串的所有单词都存了下来。对于这道题来说字符串长度通常不会太长O(n)的空间完全够用。不过这个思路如果拿到一个长文本场景比如分析一部小说里最后一个单词的长度就会浪费大量内存。更何况力扣的进阶要求里虽然没有明确说空间O(1)但作为从业者我总是习惯在写每次代码时问自己一句能不能把这部分内存省掉2.2 更优雅的双指针写法省掉内存的思路很简单既然只需要最后一个单词的长度那我从字符串末尾往前找就行了。从末尾开始先跳过所有空格然后数到下一个空格为止这段距离就是答案。def lengthOfLastWord(s: str) - int: end len(s) - 1 # 第一步从右往左跳过末尾的空格 while end 0 and s[end] : end - 1 # 此时 end 指向最后一个单词的最后一个字母 start end # 第二步继续向左移动直到遇到空格或越界 while start 0 and s[start] ! : start - 1 # 此时 start 指向最后一个单词前面的空格或者为 -1 return end - start逐行解释一下。第一个while循环是在做“定位”把指针从字符串末尾往左移动忽略掉尾部的所有空格。比如Hello World 循环结束后end会停在d上。第二个while循环是从这个位置继续往左直到遇到空格或者指针越界为止。拿Hello World举例第一个循环直接跳过因为末尾没有空格第二个循环中start从d开始往左数走到空格前停下此时end - start正好是5也就是World的长度。如果整个字符串全是空格比如 第一个循环会让end一路减到-1第二个循环因为start一开始就是-1条件不成立直接返回0。这个结果天然是正确的因为不存在单词。如果字符串是一个单词比如hello第二个循环会让start一路走到-1此时end - start 5 - 0 5也正确因为最后一个单词的长度就是整个字符串的长度。这个解法的时间复杂度同样是O(n)但空间复杂度降到了O(1)只用了两个整数变量。写起来比split版本稍长一点点但省下了整个单词列表的内存也更贴近很多面试官期望看到的“手写遍历”风格。2.3 边界条件与测试用例设计写算法题最忌讳的是“上传代码前不测试”。我见过不少人在本地跑了一个用例就提交结果因为边界条件翻车。第58题的边界条件其实非常经典我整理了一张表建议你对着这张表测一遍再提交。输入预期输出说明Hello World5常规情况末尾无空格 fly me to the moon 4首尾和中间都有多个连续空格luffy is still joyboy6中间正常空格最后一个单词较长hello5只有一个单词无空格 0单个空格 0全部是空格a1单个字母0空字符串注意表格里最后一行空字符串在LeetCode的测试数据里通常不会出现题干默认字符串至少包含一个单词。但自己在本地测试时加上这个用例没有坏处可以验证代码在极端情况下的鲁棒性。我自己的习惯是写完代码后先把这些用例一个个跑一遍再点提交。这看起来多花了三十秒实际上能避免提交两三次才通过的尴尬——提交次数多了平台会扣分面试里也会显得不严谨。3. 把一道题当跳板刷题路径与技术延伸3.1 从58题到热门100题的选择策略第58题刷完下一步该做什么很多人会直接按题号顺序往后刷从第59题开始一路刷下去。我不推荐这条路因为按题号刷题等于随机漫步今天碰到螺旋矩阵明天碰到合并区间知识点之间没有递进关系反而让大脑一直处于切换状态记忆效果很差。更聪明的做法是围绕“LeetCode热门100题”这份清单来规划。热门100题是社区里经过大量面试反馈沉淀下来的题目集合覆盖了数组、字符串、链表、二叉树、动态规划、回溯等高频考点。第58题本身不在热门100题里但它和热门100题中的很多字符串题共享基础技巧。例如第125题验证回文串、第344题反转字符串都涉及对字符串边界和字符的判断第151题反转字符串中的单词更是第58题的直接加强版它要求先逆序整个字符串再逆序每个单词本质上就是先找到单词边界再处理。所以我的建议是把第58题当成“摸底测试”做完之后去热门100题里挑字符串和数组分类下的简单题依次推进。每做一道新题问自己三个问题这道题用到的技巧和第58题有什么共性有没有新的边界条件出现我能不能用双指针的思路扩展到这道题上带着这些问题刷题每一道题都在帮上一道题加固记忆。3.2 周赛430用竞赛检验“笨鸟”成色很多刷了一阵子题的人会把“LeetCode周赛430”这类赛事当成试金石。周赛是LeetCode每周固定举办的限时编程比赛通常包含四道题难度从简单到困难递增。第430场周赛我参加过第一题往往是那种“只要手速够快就能过”的送分题难度和第58题相仿。对于“先飞的笨鸟”来说周赛是最好的检验场它逼你在有限时间内读完题、写出代码、处理边界、提交通过一整条流程下来你会非常清楚自己在压力下的编码水平。我第一次参加周赛时第一题写了快四十分钟原因不是题目难而是紧张到忘了怎么处理输入输出的细节。后来我养成了一个习惯每周提前把本周周赛的题目预告看一眼如果是字符串处理相关的简单题就先在纸上把第58题的双指针写法默写一遍。这个动作特别管用相当于赛前热身让手指记住“跳过空格—统计长度”的节奏。周赛成绩不理想不用太沮丧记住一个原则周赛不是为了拿排名是为了暴露短板。今天暴露一个明天就补一个这就是笨鸟先飞的真实含义。3.3 073爱吃香蕉的狒狒一次必要的进阶当你在字符串题上积累了一定手感之后我特别推荐去做“LeetCode 073爱吃香蕉的狒狒”这道题。很多人在刷题时会有个误区觉得简单题刷够了才能碰中等题。其实不然有一类中等题并不要求你掌握新算法而是把“猜答案”这件事结构化。爱吃香蕉的狒狒是一道典型的二分查找应用题它让你在“每小时吃K根香蕉能在H小时内吃完”的条件下去寻找最小的K。你可能会奇怪为什么从第58题这种字符串题跳到二分查找的题我来说说这两道题的内在联系。第58题训练的是“在一个线性结构里定位边界”从后往前数、跳出循环的条件判断这些基本功本质上和二分查找里的low和high指针移动是同一种心智模型。二分查找的精髓就是不断缩小搜索区间而第58题的双指针从尾部向左收缩也是在缩小搜索区间。会了前者理解后者就顺理成章。所以第58题的意义不只是一道题它是一个思维起点。从字符串边界的定位出发你可以延伸到回文串判断、单词反转、子串匹配再到二分查找这类搜索区间的经典算法。这条学习路径是有层次的每一站都是用上一站的工具来解决新问题。我把这种“笨鸟先飞”的路径总结为先在一个小技能点上做到滚瓜烂熟然后用它去撬动相邻的知识点再一步步铺开成完整知识网络。4. 常见问题与排查技巧实录4.1 写代码时最容易踩的坑这道题虽然简单但我在实际跑代码时见过不少奇怪的报错和错误输出这里挑几个典型的说一下。第一个坑用split方法后不检查列表是否为空。有的新手会直接写return len(s.split()[-1])当输入全是空格时split之后是空列表取[-1]会直接抛IndexError。力扣的测试用例一般不会给这样的输入但用自己的测试用例时很容易踩中。第二个坑忘记去掉末尾空格的循环条件。如果只做第二个循环而不先跳过末尾空格比如输入是World 你会从最后一个字符开始数先把空格数进去然后遇到r停下算长度得到结果是0而不是5。这个问题我见过很多次而且它不像空列表那样会报错而是静默返回错误答案特别容易让人一头雾水。第三个坑索引越界。手动写双指针时循环结束条件里如果用s[end]而忘记判断end 0当字符串全是空格时end会一路减到-1此时再访问s[-1]会取到字符串里的最后一个字符Python的负索引不会报错但逻辑就完全错了。这一点在Python里尤其隐蔽因为负索引会让你以为没出问题。第四个坑把! 写成is not 。这个错误初看觉得好笑但确实发生过。Python的字符串比较要用或!is比较的是对象地址不是内容。虽然字符串在Python里可能有驻留机制短字符串偶尔能工作但这是靠运气不是正确写法。4.2 阅读题解的正确姿势刷题过程中LeetCode题解区是最宝贵的资源之一但很多人不会用。我见过两类极端一类人完全不看题解自己死磕一下午另一类人每道题都先翻题解看完就觉得自己会了。这两种方式效率都不高。对于第58题这样的简单题我的建议是至少自己独立写出一种解法再来对照题解。如果一上来就看题解你失去的是“从零到一”的思考过程而这个过程恰恰是最有价值的训练。当你自己写完之后再看题解里的双指针版、用语言特性一行过的版本、用正则的版本你会发现同样一道题可以有完全不同的肌肉记忆这会打开思路。更进阶一点的做法是总结题解里的代码风格有的人会提前判空然后优雅处理有的人会把主逻辑抽成小函数。这些风格会影响你在白板面试时给面试官的印象。我在刷题早期养成的习惯是每道题的题解用自己的话重写一遍然后在评论区找点赞最高的一两个方案看看它们比我强在哪。第58题的题解区里最高赞答案经常是四行C代码简洁到让人叹服。这种“原来可以这么写”的冲击感正是推动笨鸟持续先飞的内在动力。这之后我还想多说一点如果你在未来某天刷到第151题“反转字符串中的单词”记得回头再看一眼第58题。第151题需要你把每个单词的顺序反过来但第一步依然是找到单词边界。你会发现当年那只笨鸟在简单题里反复练过的双指针技巧正是一切复杂字符串操作的地基。这也是我一直觉得第58题值得反复咀嚼的原因——它像一块最普通的砖但所有的墙都是从这样的砖开始垒起来的。