剑指 Offer 58 - I. 翻转单词顺序

题目描述

输入一个英文句子,翻转句子中单词的顺序,但单词内字符的顺序不变。为简单起见,标点符号和普通字母一样处理。例如输入字符串”I am a student. “,则输出”student. a am I”。

示例 1:

1
2
输入: "the sky is blue"
输出: "blue is sky the"

示例 2:

1
2
3
输入: "  hello world!  "
输出: "world! hello"
解释: 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。

示例 3:

1
2
3
输入: "a good   example"
输出: "example good a"
解释: 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

说明:

  • 无空格字符构成一个单词。
  • 输入字符串可以在前面或者后面包含多余的空格,但是反转后的字符不能包括。
  • 如果两个单词间有多余的空格,将反转后单词间的空格减少到只含一个。

注意: 本题与主站 151 题相同:https://leetcode-cn.com/problems/reverse-words-in-a-string/

注意: 此题对比原题有改动


算法

(字符串翻转) $O(n)$

  1. 使用双指针算法先翻转每个单词
  2. 然后翻转整个字符串

但在字符串的开头、中间、结尾都可能会有空格,我们需要把这三部分多余的空格去掉,只保留每个单词之间的空格。而且题目要求使用 $O(1)$ 的空间完成,所以只能复用字符串,用一个指针 $k$ 从前往后存储有效的字符部分,翻转结束后 $k$ 后面多余的字符都删掉。

$[i, j]$ 单词部分放到 $[k, t]$ 位置上

时间复杂度

整个字符串需要被扫描两遍,所以时间复杂度为 $O(n)$

空间复杂度

$O(1)$

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class Solution {
public:
string reverseWords(string s) {
int k = 0;
for (int i = 0; i < s.size(); i ++ ) {
if (s[i] == ' ') continue;
// k 指向单词的开头,t 指向这个单词末尾的下一个位置
int j = i, t = k;
while (j < s.size() && s[j] != ' ') s[t ++ ] = s[j ++ ];
// 翻转单词 $s[k]~s[t - 1]$
reverse(s.begin() + k, s.begin() + t);
s[t ++ ] = ' ';
i = j, k = t;
}
// 翻转结束后 k 指向的是下一个单词的开头,现在让它指向前一个位置的空格
if (k) k -- ;
// 删除空格以及下标 k 后面的字符
s.erase(s.begin() + k, s.end());
reverse(s.begin(), s.end());
return s;
}
};
Author: tonngw
Link: https://tonngw.com/2022/07/09/剑指 Offer/剑指 Offer 58 - I. 翻转单词顺序/
Copyright Notice: All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.