HJ31. 单词倒排
题目描述
对字符串中的所有单词进行倒排,单词之间以空格分隔。"单词"由字母(A-Za-z)构成;字符串中的非字母字符(空格、数字、标点等)视为单词间的分隔符。
输入格式
- 一行字符串(可能含空格、数字、标点)。
输出格式
- 倒排后的单词序列,单词间以单个空格分隔。
示例
示例 1
输入:
I am a student
输出:
student a am I
示例 2
输入:
$bo*y gi!r#l
输出:
l r gi y bo
说明:
$bo*y gi!r#l 中字母单词为 bo、y、gi、r、l,倒排为 l r gi y bo。
解题思路
第一步:理解问题本质
把连续字母视为一个单词,非字母字符全部视为分隔符,将单词序列倒序后以单空格连接。
第二步:暴力解法(手动扫描)
手动遍历字符串,遇到字母收集成单词,遇到非字母结束当前单词,再反转单词列表。
问题:需要自己维护状态,处理连续分隔符、开头结尾分隔符等边界较繁琐。
第三步:优化解法(正则提取)
用正则 re.findall(r'[A-Za-z]+', s) 一次提取所有连续字母单词,自动跳过所有非字母分隔符。
第四步:最优解法(正则 + 反转拼接)
提取单词列表后直接反转,用空格连接即可。
words = re.findall(r'[A-Za-z]+', s) # 提取所有单词
print(' '.join(words[::-1]))
完整代码实现
本题无可视化步骤:代码里没有可逐帧展示的过程(执行轨迹已经录下来, 但局部变量里找不到推进中的中间状态 —— 比如全篇就是一次正则替换或一次切片)。
import sys
import re
def solve() -> None:
"""主求解函数"""
s = sys.stdin.readline().strip()
words = re.findall(r'[A-Za-z]+', s) # 提取所有单词
print(' '.join(words[::-1]))
if __name__ == "__main__":
solve()
示例推演
以示例 2 $bo*y gi!r#l 为例:
提取单词:
正则 [A-Za-z]+ 匹配所有连续字母:
$非字母,跳过bo→ 单词*非字母,跳过y→ 单词非字母,跳过gi→ 单词!非字母,跳过r→ 单词#非字母,跳过l→ 单词
得到单词列表 ['bo', 'y', 'gi', 'r', 'l']。
反转并连接:
['l', 'r', 'gi', 'y', 'bo'] → l r gi y bo。
复杂度分析
| 解法 | 时间复杂度 | 空间复杂度 | 说明 |
|---|---|---|---|
| 手动扫描 | O(L) | O(L) | 需处理多种分隔符边界 |
| 正则提取 | O(L) | O(L) | 正则自动处理分隔符 |
L 为字符串长度
易错点总结
1. 单词仅由字母构成
单词只包含 A-Za-z,数字、标点、空格都不是单词的一部分,而是分隔符。
2. 连续分隔符
多个非字母字符连续出现时不能产生空单词,正则 + 自动保证不会匹配到空串。
3. 反转整个列表
是单词列表整体反转,而不是每个单词内部反转,注意 words[::-1] 的位置。
4. 输出用单空格
倒排后的单词之间以单个空格分隔,不要保留原分隔符。
扩展思考
- 正则
[A-Za-z]+匹配连续字母,是处理"按某类字符分割"的通用技巧。 - 若单词允许其他字符,只需调整正则的字符集即可,例如
[\w]+。