在 LeetCode 简单题中,58 题“最后一个单词的长度”看似基础,却能很好地考察对字符串处理的细节把控和算法效率的思考。题目要求我们在一个由单词和空格组成的字符串中,返回最后一个纯字母单词的长度(单词不含空格,且被若干空格分隔)。本文将带来两种主流解法,从思路、代码到优劣对比,帮你彻底吃透这道题。
题目回顾
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1:输入 s = "Hello World",输出 5(最后一个单词为 "World");
示例 2:输入 s = " fly me to the moon ",输出 4(最后一个单词为 "moon");
示例 3:输入 s = "luffy is still joyboy",输出 6(最后一个单词为 "joyboy")。
解法一:正则分割 + 数组取值(简洁高效款)
思路分析
这种解法的核心是利用字符串处理方法和正则表达式,将混乱的字符串转化为干净的单词数组,再直接取最后一个单词的长度。核心步骤分为两步:
-
用
trim()方法去除字符串首尾的空格,避免首尾空格对分割结果的干扰; -
用正则表达式
/\s+/分割字符串,该正则能匹配一个或多个连续空格,完美解决“多个空格分隔单词”的问题,得到仅包含有效单词的数组; -
取数组最后一个元素,返回其长度即可。
代码实现(TypeScript)
function lengthOfLastWord_1(s: string): number {
// 去除首尾空格后,按一个或多个空格分割为单词数组
const sArr: string[] = s.trim().split(/\s+/);
// 返回最后一个单词的长度
return sArr[sArr.length - 1].length;
};
关键细节说明
-
为什么不用
split(' ')?直接用单个空格分割会导致连续空格产生空字符串(如示例 2 分割后会出现["", "fly", "me", "", "", "to", ...]),后续还要过滤空字符串,繁琐且低效; -
trim()的作用?若不使用trim(),字符串开头的空格会被/\s+/匹配,分割后数组第一个元素为空字符串(如示例 2 不trim()会得到["", "fly", "me", ...]),虽不影响取最后一个元素,但会多一步无效分割; -
时间复杂度:
trim()、split()均为 O(n)(n 为字符串长度),整体时间复杂度 O(n);空间复杂度 O(m)(m 为单词个数),因为需要存储分割后的单词数组。
解法二:反向遍历(空间最优款)
思路分析
解法一虽简洁,但额外占用了数组空间。如果追求空间最优,可采用反向遍历的思路:从字符串末尾开始,先跳过所有末尾空格,找到最后一个单词的结尾,再继续反向遍历统计单词长度,直到遇到空格或遍历结束。核心步骤:
-
定义索引
index指向字符串最后一个字符(s.length - 1); -
反向遍历跳过末尾空格:当
index >= 0且当前字符是空格时,index--; -
统计最后一个单词长度:当
index >= 0且当前字符不是空格时,计数器res++并index--; -
返回计数器
res,即为最后一个单词的长度。
代码实现(TypeScript)
function lengthOfLastWord_2(s: string): number {
let index = s.length - 1;
// 跳过末尾的所有空格
while (s[index] === ' ' && index >= 0) {
index--;
}
let res = 0;
// 统计最后一个单词的长度
while (index >= 0 && s[index] !== ' ') {
res++;
index--;
}
return res;
};
关键细节说明
-
边界条件处理:
index >= 0是避免遍历越界(当字符串全为空格时,第一个循环会将index减至 -1,第二个循环不执行,返回 0,符合题意); -
空间优势:全程仅使用两个变量(
index和res),空间复杂度为 O(1); -
时间复杂度:最坏情况下需遍历整个字符串(如字符串无空格),时间复杂度仍为 O(n),但无额外空间开销。
两种解法对比与适用场景
| 解法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|---|---|
| 正则分割 + 数组取值 | O(n) | O(m) | 代码简洁、易理解,开发效率高 | 额外占用数组空间 | 日常开发、算法题快速解题,对空间要求不高的场景 |
| 反向遍历 | O(n) | O(1) | 空间最优,无额外开销 | 代码稍长,需处理边界条件 | 空间敏感场景,如大规模字符串处理、面试中追求最优空间复杂度 |
常见坑点提醒
-
忽略全空格字符串:如输入
" ",此时无有效单词,应返回 0,两种解法均能正确处理; -
多个连续空格分隔:解法一靠
/\s+/解决,解法二靠反向跳空处理,均需避免将连续空格计入单词长度; -
单词仅一个的情况:如输入
"Hello"或" Hello",两种解法均能正确返回 5。
总结
LeetCode 58 题虽为简单题,但两种解法对应了不同的优化方向:解法一以空间换简洁,适合快速落地;解法二以时间换空间,适合追求极致性能。在面试中,若能同时给出两种解法并分析其优劣,会更受面试官青睐。
其实字符串处理类题目核心在于对细节的把控,无论是正则的灵活运用,还是遍历方向的选择,都需要结合场景权衡。希望本文能帮你掌握这道题的本质,在后续同类题目中举一反三。