题目描述
思路解析
一句话答案:LeetCode 2114 句子中的最多单词数:题面保证单词间正好一个空格、首尾无空格,于是每句单词数就是空格数加一,逐句数空格取最大即答案。时间 O(总字符数)、空间 O(1)。
一批句子,求单个句子里最多能塞下几个单词
给你一个字符串数组 sentences,每个元素是一句话,单词之间正好一个空格,开头结尾都没有空格,只含小写字母和空格。要返回的是:这些句子里,单个句子最多有几个单词。题面 sentences=["i can do it","red fox","go"] 的答案是 4——三句分别有 4、2、1 个单词,最多的是第一句的 4 个。注意问的是最多那一句,不是把三句的单词数加起来。
真按空格切开再数长度,答案也对,只是绕了远路
把每句按空格切成一个单词列表、再数列表长度,三句比出最大——这样做能得对答案,只是切分这一步要另开一个列表、逐段把字符拷进去,句子越长拷得越多。可题目给了很强的格式保证:单词之间正好一个空格、首尾没有多余空格。有了这份规范,其实不必真的切分,就能把单词数数出来。
相邻单词之间只夹一个空格,空格数加一就是单词数
把一句话摆开看:单词、空格、单词、空格……每两个相邻单词之间夹着且只夹着一个空格。所以 k 个空格恰好把句子隔成 k+1 段,每段正是一个单词。数出一句有几个空格,加一,就是这句的单词数,根本不用把单词一个个拆出来。i can do it 里有 3 个空格,单词数就是 3+1=4;go 一个空格都没有,就是 0+1=1。这个加一别丢:k 个空格切出的是 k+1 段,不是 k 段。
一句一句扫过去,每句数空格加一,一路取最大值
拿一个变量存到目前为止见过的最大单词数,起手记 0。逐句处理:数出这句有几个空格,加一得到本句单词数,和当前最大值比一下,更大就更新。所有句子过完,这个最大值就是答案。参考代码把整套逻辑压成一行 1 + max(s.count(' ') for s in sentences):对每个句子 s 调 s.count(' ') 数空格,外面套 max 取所有句子空格数里的最大,最后统一加一。因为每句都加一,取完最大再加一和逐句加一等价。
三句话里哪句单词最多
第一句 i can do it,空格落在 i 和 can、can 和 do、do 和 it 之间,共 3 个,单词数 3+1=4,最大值从 0 刷到 4。第二句 red fox,只有 red 和 fox 之间一个空格,单词数 1+1=2,没超过 4,最大值不动。第三句 go,一个空格也没有,单词数 0+1=1,同样没超过 4。三句过完,最大值停在 4,正是题面给的答案。
数完空格别忘记那个加一
设所有句子的字符总长为 L,数空格是把每个字符看一遍,时间 O(L);只用了最大值和几个计数变量,空间 O(1),不随输入变大。几处地方容易磕绊:只有一个单词、一个空格都没有的句子,单词数是 0+1=1 而不是 0,漏了加一会把每句都少数一个;最大值可能落在任何一句,得逐句比过去,不能只盯第一句或最后一句。还有个前提——数空格加一只在题面这种规范格式下成立:句子非空、单词间正好一个空格、首尾无空格。要是允许连续空格或首尾留空,空格数就会虚高,得改成扫描时只在从空格跨进字母的那一刻记一个新单词。
▶ 动画逐步走查(文字版)——想跟着上方动画一帧帧对照就展开
记牢这一句:单词数等于空格数加一。因为格式规范,数空格比切单词省事得多。下面就一句一句地扫过去,每句数出空格、加一,再和当前最大值比一比。
开扫 句1 · i can do it:开始扫 句1,也就是 i can do it。把这句拆成一排字符格,空格用一个小方块 ␣ 标出来。空格计数从 0 开始;哪怕一个空格都没有,一句话里至少也有一个单词,所以单词数先记成 1。紫色指针待会儿从最左边一格一格往右走。
扫到字母 i · 句1 第 1 格:指针走到第 1 格,是字母 i,不是空格,空格计数不动,仍是 0。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 1。
扫到空格 · 句1 第 2 格:指针走到第 2 格,这是一个空格,把它标绿。空格计数从 0 加到 1。空格是单词的分隔符,数到 1 个空格,就意味着眼下已经分出 2 段单词。
扫到字母 c · 句1 第 3 格:指针走到第 3 格,是字母 c,不是空格,空格计数不动,仍是 1。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 2。
扫到字母 a · 句1 第 4 格:指针走到第 4 格,是字母 a,不是空格,空格计数不动,仍是 1。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 2。
扫到字母 n · 句1 第 5 格:指针走到第 5 格,是字母 n,不是空格,空格计数不动,仍是 1。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 2。
扫到空格 · 句1 第 6 格:指针走到第 6 格,这是一个空格,把它标绿。空格计数从 1 加到 2。空格是单词的分隔符,数到 2 个空格,就意味着眼下已经分出 3 段单词。
扫到字母 d · 句1 第 7 格:指针走到第 7 格,是字母 d,不是空格,空格计数不动,仍是 2。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 3。
扫到字母 o · 句1 第 8 格:指针走到第 8 格,是字母 o,不是空格,空格计数不动,仍是 2。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 3。
扫到空格 · 句1 第 9 格:指针走到第 9 格,这是一个空格,把它标绿。空格计数从 2 加到 3。空格是单词的分隔符,数到 3 个空格,就意味着眼下已经分出 4 段单词。
扫到字母 i · 句1 第 10 格:指针走到第 10 格,是字母 i,不是空格,空格计数不动,仍是 3。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 4。
扫到字母 t · 句1 第 11 格:指针走到第 11 格,是字母 t,不是空格,空格计数不动,仍是 3。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 4。
句1 结算 · 单词 4:句1 扫完了,一共 3 个空格,单词数就是 3 加 1,等于 4。它比之前记的最大值 0 还大,把最大值刷新成 4。把这句的结果记进右边面板,接着看下一句。
开扫 句2 · red fox:开始扫 句2,也就是 red fox。把这句拆成一排字符格,空格用一个小方块 ␣ 标出来。空格计数从 0 开始;哪怕一个空格都没有,一句话里至少也有一个单词,所以单词数先记成 1。紫色指针待会儿从最左边一格一格往右走。
扫到字母 r · 句2 第 1 格:指针走到第 1 格,是字母 r,不是空格,空格计数不动,仍是 0。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 1。
扫到字母 e · 句2 第 2 格:指针走到第 2 格,是字母 e,不是空格,空格计数不动,仍是 0。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 1。
扫到字母 d · 句2 第 3 格:指针走到第 3 格,是字母 d,不是空格,空格计数不动,仍是 0。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 1。
扫到空格 · 句2 第 4 格:指针走到第 4 格,这是一个空格,把它标绿。空格计数从 0 加到 1。空格是单词的分隔符,数到 1 个空格,就意味着眼下已经分出 2 段单词。
扫到字母 f · 句2 第 5 格:指针走到第 5 格,是字母 f,不是空格,空格计数不动,仍是 1。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 2。
扫到字母 o · 句2 第 6 格:指针走到第 6 格,是字母 o,不是空格,空格计数不动,仍是 1。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 2。
扫到字母 x · 句2 第 7 格:指针走到第 7 格,是字母 x,不是空格,空格计数不动,仍是 1。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 2。
句2 结算 · 单词 2:句2 扫完了,一共 1 个空格,单词数就是 1 加 1,等于 2。它没有超过当前最大值 4,最大值保持不变。把这句的结果记进右边面板,接着看下一句。
开扫 句3 · go:开始扫 句3,也就是 go。把这句拆成一排字符格,空格用一个小方块 ␣ 标出来。空格计数从 0 开始;哪怕一个空格都没有,一句话里至少也有一个单词,所以单词数先记成 1。紫色指针待会儿从最左边一格一格往右走。
扫到字母 g · 句3 第 1 格:指针走到第 1 格,是字母 g,不是空格,空格计数不动,仍是 0。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 1。
扫到字母 o · 句3 第 2 格:指针走到第 2 格,是字母 o,不是空格,空格计数不动,仍是 0。字母在单词内部,只有空格才是切分点,所以看到字母只管往右走。当前单词数按空格数加一算,是 1。
句3 结算 · 单词 1:句3 扫完了,一共 0 个空格,单词数就是 0 加 1,等于 1。它没有超过当前最大值 4,最大值保持不变。把这句的结果记进右边面板,三句全数完了。
回放 · 最多单词数 = 4:三句全部数完:i can do it 有 4 个单词,red fox 有 2 个单词,go 有 1 个单词。里面最多的是 i can do it 的 4 个单词,舞台上把它的空格全标绿给你看,3 个空格切出 4 段。所以整组句子里单个句子的最多单词数就是 4。
边界想清:单个词记 1、含一个空格记 2、最大值可能出现在任意一句,得逐句比过去。
面试重点:规范格式下单词数等于空格数加一、非规范时改判从空格到字母的边界、时间线性空间常数。
参考代码
from __future__ import annotationsfrom typing import *from collections import *from functools import *from itertools import *from math import *from heapq import *from bisect import *class TreeNode: def __init__(self, val=0, left=None, right=None): self.val = val self.left = left self.right = rightclass ListNode: def __init__(self, val=0, next=None): self.val = val self.next = nextclass Solution: def mostWordsFound(self, sentences: List[str]) -> int: return 1 + max(s.count(' ') for s in sentences)复杂度
- 时间:O(总字符数),设所有句子的字符总长为 L。每个字符最多被看一次:数空格就是把字符扫一遍。句子个数与总字符数都被这一遍覆盖,整体随总字符数线性增长
- 空间:O(1),只用了空格计数、当前单词数、最大值这几个变量,不随输入规模增长。没有额外开与输入等长的数组或哈希表
易错点
面试追问把动画讲成自己的话
追问这题的核心思路是什么?
追问如果句子里可能有连续空格或首尾空格,还能这么做吗?
追问复杂度是多少?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
下一个排列
LeetCode 31 · 中等 · 沿着 数组套路 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题