最后一个单词的长度
在 LeetCode 上查看 ↗语音讲解
开车或通勤时可听,跟着思路走一遍
题目描述
给你一个字符串 s,由若干单词组成,单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。
单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。
示例 1
示例 2
示例 3
模拟答题者思考
1. 最直接:按空格 split 成单词数组,取最后一个元素的长度——逻辑正确,但会构造中间数组,空间 O(n),且从左到右处理了所有单词,而我们只关心最后一个。
2. 重复在哪里?从左扫描需要区分「当前在第几个单词」,末尾还有多余空格时还要额外判断「是否已越过最后一个单词」;其实答案只取决于从右往左第一个连续字母段的长度。
3. 关键转化:指针 i 从末尾出发,先跳过所有尾部空格,再统计连续非空格字符个数即为最后一个单词长度。
4. 手推 " fly me to the moon ":i 从末尾跳过两个空格,停在 'n',依次数 m,o,o,n 得 cnt=4,再遇到空格停止。
5. 只需一次线性扫描,时间 O(n)、额外空间 O(1);题面保证至少有一个单词,故跳过尾部空格后一定能数到至少一个字母。
变量语义(先读这三句再编码)
| 变量 | 类型 | 语义(三句法) |
|---|---|---|
i | int | 定义:从字符串末尾向左扫描的下标指针 维护:初始为 len(s) - 1,单调递减,每个字符最多访问一次更新:跳过尾部空格时 i--;统计单词长度时每遇到一个字母 i-- 且 cnt++ |
cnt | int | 定义:当前已扫描到的最后一个单词的字符个数 维护:初始为 0;仅在「连续非空格段」内递增 更新:当 s[i] != ' ' 时 cnt += 1;遇到空格或 i < 0 时停止,cnt 即为答案 |
s | str | 定义:输入字符串,由英文字母与空格组成,至少含一个单词 维护:只读,通过 s[i] 判断当前字符是字母还是空格更新:不修改原串;尾部空格与单词间空格均通过 i 的左移跳过 |
落码步骤
1. 令 i = len(s) - 1,cnt = 0
2. 跳过尾部空格:当 i >= 0 且 s[i] == ' ' 时,i -= 1
3. 统计最后一个单词:当 i >= 0 且 s[i] != ' ' 时,cnt += 1,i -= 1
4. 返回 cnt
代码实现
class Solution:
def lengthOfLastWord(self, s: str) -> int:
i = len(s) - 1
# 跳过末尾空格
while i >= 0 and s[i] == ' ':
i -= 1
cnt = 0
# 统计最后一个单词长度
while i >= 0 and s[i] != ' ':
cnt += 1
i -= 1
return cnt
class Solution {
public:
int lengthOfLastWord(string s) {
int i = (int)s.size() - 1;
// 跳过末尾空格
while (i >= 0 && s[i] == ' ') --i;
int cnt = 0;
// 统计最后一个单词长度
while (i >= 0 && s[i] != ' ') {
++cnt;
--i;
}
return cnt;
}
};
// 时间 O(n),空间 O(1)
复杂度分析
O(n)
O(1)
常见坑
忘记跳过尾部空格:若直接从末尾计数,会把末尾空格也算进长度,如 "moon " 会错成 2 而非 4。
用 split() 时写成 split(' '):连续空格会产生空串,最后一个「单词」可能是空字符串,导致长度为 0。
从左扫描时 off-by-one:需要额外状态判断「是否已进入最后一个单词」,逻辑比从右扫描更绕,容易写错边界。
必测边界 Case
s = "Hello World" → 5(最后一个单词 World)
s = " fly me to the moon " → 4(必须先跳过尾部空格再计数)
s = "a" → 1;s = "word" → 4(跳过空格循环 0 次,直接进入计数)
s = "a b" → 1(只数最后一个字母段 b)
s = "luffy is still joyboy" → 6(joyboy 共 6 个字母)