LeetCode 418 - 屏幕可显示句子的数量


文章目录
- 摘要
- 描述
- 题解答案
- 题解代码分析
- 示例测试及结果
- 时间复杂度
- 空间复杂度
- 总结
摘要
今天我们来聊一道挺有意思的题目 —— LeetCode 418《屏幕可显示句子的数量(Sentence Screen Fitting)》。这道题乍一看像是字符串模拟题,但如果你一股脑儿暴力模拟,性能会很糟糕。想要高效求解,就得找到循环规律,而不是一格格地去填屏幕。下面我会用 Swift 实现完整的解法,并结合一个实际场景来说明这类问题在“文本排版引擎”或“信息显示屏系统”中的应用。

描述
题目要求是这样的:
我们有一个由若干单词组成的句子,比如 ["hello", "world"]。屏幕有固定的行数 rows 和列数 cols。每行从左到右显示句子内容,单词之间用一个空格隔开。
要求计算:整个句子可以在屏幕上完整地显示多少次。
需要注意:
- 单词不能被拆开跨行。
- 每次从句子的第一个单词重新开始。
- 屏幕装不下一个完整单词就要换行。
举个例子:
sentence = ["hello", "world"]
rows = 2
cols = 8
第一行能显示 "hello"(5个字符)+空格(1)=6个位置,剩下2个空位不够放 "world",所以换行。
第二行能显示 "world"(5个字符),剩下3个空位,不够继续放 "hello"。
这时正好显示了整个句子一次,因此答案是 1。
题解答案
最直观的思路是:一行行模拟,把每个单词往里塞,但这样复杂度是 O(rows × avg_word_length),对于大输入非常慢。
所以我们换一种方式——字符串拼接 + 模拟滑动。
思路如下:
-
把句子拼接成一个带空格的长字符串,比如
s = "hello world "。 -
维护一个指针
pos表示我们在整个字符串中的当前位置。 -
对每一行:
- 让
pos += cols(表示当前行能放下这么多字符) - 如果
s[pos % len]是空格,就说明刚好到单词边界,pos++ - 如果不是空格,说明中间断了一个单词,需要回退到单词起始位置(向左走直到遇到空格)
- 让
-
最后,整个句子在屏幕上完整显示的次数为
pos / len
这个算法其实相当于把所有行“拼”成一个大字符串,通过位置计算得到循环次数。

题解代码分析
我们用 Swift 实现这个思路,代码如下:
import Foundationclass Solution {func wordsTyping(_ sentence: [String], _ rows: Int, _ cols: Int) -> Int {// 将句子拼成一个带空格的长字符串let s = sentence.joined(separator: " ") + " "let n = s.countvar pos = 0 // 当前在字符串中的位置for _ in 0..<rows {pos += cols // 当前行放下这么多字符// 如果正好落在空格上,跳过空格if s[s.index(s.startIndex, offsetBy: pos % n)] == " " {pos += 1} else {// 向左回退直到找到空格while pos > 0 && s[s.index(s.startIndex, offsetBy: (pos - 1) % n)] != " " {pos -= 1}}}// pos / n 就是完整句子在屏幕上显示的次数return pos / n}
}// 可运行 Demo
let solution = Solution()
let result = solution.wordsTyping(["hello", "world"], 2, 8)
print("可显示完整句子的次数是:\(result)")
代码解析
sentence.joined(separator: " ") + " ":拼接句子形成循环字符串,方便处理边界。pos累加每行的列数,模拟光标往后走。- 判断当前落点是不是空格,如果不是,就往回走,保证不会拆单词。
- 最终除以句子长度,就是能完整显示多少次。
这种思路之所以高效,是因为每行的扫描都是基于字符长度,而不是单词遍历,相当于用一个滑动窗口在循环字符串上移动。
示例测试及结果
示例1:
let result1 = solution.wordsTyping(["hello", "world"], 2, 8)
print(result1) // 输出 1
示例2:
let result2 = solution.wordsTyping(["a", "bcd", "e"], 3, 6)
print(result2) // 输出 2
解释:
屏幕宽 6,高 3:
第1行: a bcd
第2行: e a
第3行: bcd e
完整显示了 2 次句子。
示例3:
let result3 = solution.wordsTyping(["I", "had", "apple", "pie"], 4, 5)
print(result3) // 输出 1
时间复杂度
每一行我们最多往回移动若干字符,但整体最多扫描字符串的长度上限。
因此时间复杂度是:
O(rows)
因为 pos 只向前移动(或小范围回退),不会超过 rows * cols。
空间复杂度
除了拼接字符串和几个变量外,没有额外结构,空间复杂度为:
O(1)
总结
这道题核心在于:不要一行一行地暴力塞,而是把句子看作一个循环字符串,用一个移动的指针去模拟“打印”过程。
这种技巧在实际场景中非常常见,比如:
- 移动设备的文本滚动显示:小屏幕上反复显示一句宣传语。
- LED 信息屏或跑马灯系统:同一串文字不断循环显示。
- 电子阅读器分页引擎:根据屏幕尺寸动态计算能显示多少内容。
