【字符串】——翻转字符串中的单词

Python反转字符串中单词的三种解法

151. 反转字符串中的单词

题目难度

中等

题目描述

给你一个字符串s,请你反转字符串中单词的顺序。

单词是由非空格字符组成的字符串。s中使用至少一个空格将字符串中的单词分隔开。

返回单词顺序颠倒且单词之间用单个空格连接的结果字符串。

注意:输入字符串s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

示例

示例 1

输入s = "the sky is blue"
输出"blue is sky the"

示例 2

输入s = " hello world "
输出"world hello"
解释:反转后的字符串中不能存在前导空格和尾随空格。

示例 3

输入s = "a good example"
输出"example good a"
解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。

提示信息

  • 1 <= s.length <= 104
  • s包含英文大小写字母、数字和空格 ' '
  • s中至少存在一个单词。

解题思路

  1. 去除多余空格

    • 首先,去除输入字符串s中的前导空格、尾随空格以及单词之间的多余空格。可以通过遍历字符串,使用一个新的字符串来存储处理后的结果。当遇到非空格字符时,将其添加到新字符串中;当遇到空格且新字符串不为空时,再添加一个空格。这样可以确保新字符串中单词之间只有一个空格。
  2. 反转整个字符串

    • 对去除多余空格后的字符串进行反转。可以使用双指针的方法,一个指针从字符串的开头开始,另一个指针从字符串的末尾开始,不断交换两个指针所指的字符,直到两个指针相遇。
  3. 反转每个单词

    • 再次遍历反转后的字符串,确定每个单词的边界。当遇到空格时,说明一个单词结束,可以对这个单词进行反转。同样可以使用双指针的方法,一个指针指向单词的开头,另一个指针指向单词的末尾,不断交换两个指针所指的字符,直到两个指针相遇。

解法一:反转两次

  • 先删除空白
  • 整个反转
  • 单词反转,转回来了
class Solution:
    def reverseWords(self, s: str) -> str:
        # 删除前后空白
        s = s.strip()
        # 反转整个字符串
        s = s[::-1]
        # 将字符串拆分成单词,并且反转每个单词
        s = ' '.join(word[::-1] for word in s.split())
        return s
  • 因为字符串在python时是不可变类型,所以反转单词时,需要先将其转换成列表,然后通过join函数再将其转换成列表,所以空间复杂度不是O(1)O(1)O(1)

解法二:双指针反转

class Solution:
    def reverseWords(self, s: str) -> str:
        # 将字符串char拆分成单词 '' ,即转换成列表类型
        # s = '  hello    world  '
        words = s.split()  # words = ['hello','world']
        # 双指针反转单词'位置'
        left, right = 0, len(words) - 1
        while left < right:
            words[left], words[right] = words[right], words[left]
            left += 1
            right -= 1
        
        # words = ['world','hello']
        return ' '.join(words)       

解法三:拆分字符串+反转列表

class Solution:
    def reverseWords(self, s):
        words = s.split()  # char ---> list = ['','','']
        words = words[::-1] # 切片快速反转单词位置
        return ' '.join(words)
         

以下是对三种解法的详细分析:

解法一:反转两次

  1. 思路

    • 首先使用strip()方法去除字符串前后的空白。
    • 然后通过切片操作s[::-1]反转整个字符串。
    • 最后,使用列表推导式将字符串按空格分割成单词列表,对每个单词进行反转,再用join方法将反转后的单词重新组合成字符串。
  2. 优点

    • 代码较为简洁,利用了 Python 的内置函数和切片操作,容易理解。
  3. 缺点

    • 正如你所说,由于字符串在 Python 中是不可变类型,在反转单词时需要将字符串转换为列表再转换回字符串,这会带来一定的开销,并且空间复杂度不是 O(1)O(1)O(1)
  4. 时间复杂度

    • strip()的时间复杂度为 O(n)O(n)O(n),其中 n 是字符串的长度。
    • 切片操作s[::-1]的时间复杂度为 O(n)O(n)O(n)
    • 分割字符串和对每个单词进行反转的时间复杂度也为 O(n)O(n)O(n),因为涉及遍历整个字符串。所以总体时间复杂度为 O(n)O(n)O(n)
  5. 空间复杂度

    • 由于需要创建新的列表来存储反转后的单词,所以空间复杂度不是 O(1)O(1)O(1),具体取决于字符串中的单词数量和长度。

解法二:双指针反转

  1. 思路

    • 首先使用split()方法将字符串拆分成单词列表。
    • 然后使用双指针的方法交换单词列表中单词的位置,实现单词顺序的反转。
    • 最后,使用join方法将反转后的单词列表重新组合成字符串。
  2. 优点

    • 不需要对每个单词进行单独的反转操作,只需要进行一次双指针反转单词位置的操作,相对高效。
    • 没有创建额外的数据结构来存储反转后的单词,空间复杂度相对较低。
  3. 缺点

    • 代码相对解法一稍微复杂一些,需要理解双指针的操作。
  4. 时间复杂度

    • split()方法的时间复杂度为 O(n)O(n)O(n),其中 n 是字符串的长度。
    • 双指针反转单词位置的操作时间复杂度为 O(m)O(m)O(m),其中 m 是单词的数量。由于单词数量通常小于字符串长度,所以总体时间复杂度为 O(n)O(n)O(n)
  5. 空间复杂度

    • 只创建了一个单词列表,没有创建其他额外的数据结构,空间复杂度取决于单词的数量和长度,相对较低,可以认为接近 O(m)O(m)O(m),其中 m 是单词的数量。

解法三:拆分字符串 + 反转列表

  1. 思路

    • 与解法二类似,首先使用split()方法将字符串拆分成单词列表。
    • 然后直接使用切片操作words[::-1]反转单词列表。
    • 最后,使用join方法将反转后的单词列表重新组合成字符串。
  2. 优点

    • 代码简洁,利用了切片操作快速反转单词列表。
  3. 缺点

    • 与解法一类似,需要将字符串拆分成单词列表,可能会带来一定的开销。并且空间复杂度不是 O(1)O(1)O(1)
  4. 时间复杂度

    • split()方法的时间复杂度为 O(n)O(n)O(n),其中 n 是字符串的长度。
    • 切片操作words[::-1]的时间复杂度为 O(m)O(m)O(m),其中 m 是单词的数量。总体时间复杂度为 O(n)O(n)O(n)
  5. 空间复杂度

    • 创建了一个单词列表,空间复杂度取决于单词的数量和长度,不是 O(1)O(1)O(1)

综上所述,三种解法的时间复杂度都是 O(n)O(n)O(n),但解法二在空间复杂度上相对较低,并且代码也较为高效。解法一和解法三虽然代码简洁,但在空间复杂度上有一定的不足。在实际应用中,可以根据具体情况选择合适的解法。

切片操作是只能对列表进行么?

切片操作words[::-1]的时间复杂度为 O(m)O(m)O(m),其中 mmm 是切片操作所作用的序列的长度,在这个例子中就是单词列表words中单词的数量。

切片操作不仅可以对列表进行,也可以对字符串这种数据类型进行。对于字符串的切片操作,其语法和对列表的切片操作类似,例如:

s = "hello world"
print(s[::-1])  
print(s[1:5])  

在上述代码中,s[::-1]会将字符串s反转,得到dlrow ollehs[1:5]会截取字符串s中从索引为1的字符到索引为4的字符,得到ello

字符串切片操作的时间复杂度也是与切片的长度相关的,对于长度为 nnn 的字符串,切片操作的时间复杂度通常为 O(k)O(k)O(k),其中 kkk 是切片的长度,也就是切片操作所选取的字符数量。在最坏的情况下,如果切片选取了整个字符串,时间复杂度就是 O(n)O(n)O(n)

所以,切片操作是一种非常方便且高效的操作,可以用于多种序列类型的数据,包括列表、字符串、元组等,能够帮助我们灵活地获取和处理序列中的部分数据。

### C++ 中实现字符串翻转的方法 #### 使用 `std::reverse` 函数 为了简化开发过程并利用已有的高效实现,可以直接使用 `<algorithm>` 头文件中提供的 `std::reverse` 函数来完成字符串翻转操作。下面是一段简单的代码示例: ```cpp #include <iostream> #include <string> #include <algorithm> int main() { std::string str = "hello"; std::reverse(str.begin(), str.end()); std::cout << str << std::endl; } ``` 这段代码展示了如何通过调用 `std::reverse` 来快速有效地逆转给定的字符串变量 `str` 的内容[^1]。 #### 手动交换字符位置的方式 如果希望更深入理解底层机制,则可以通过遍历字符串的一半长度,并逐个交换对应索引处的字符来进行翻转。这里给出了一种不依赖于 STL 库的手工实现方式: ```cpp void reverseString(std::string &s) { int n = s.length(); for (int i = 0; i < n / 2; ++i){ char temp = s[i]; s[i] = s[n - i - 1]; s[n - i - 1] = temp; } } // 测试该函数 int main(){ std::string testStr = "example"; reverseString(testStr); std::cout<<testStr<<'\n'; } ``` 此方法同样实现了字符串的完全倒置功能,适用于那些想要学习更多关于指针运算以及数组访问细节的学习者。 #### 特殊情况下的字符串翻转——单词顺序保持不变而整体逆序排列 对于某些特定应用场景下可能遇到的要求保留原有单词内部字母次序的同时仅改变整个句子结构的情况,可以采用如下策略解决: ```cpp #include <sstream> #include <vector> using namespace std; string ReverseWords(string input) { stringstream ss(input); vector<string> words; string word, result=""; while(ss >> word){ // 将输入按空格分割成多个子串存入向量words中 words.push_back(word); } for(auto it=words.rbegin();it!=words.rend();++it){ if(it != --words.rend()){ result += *it + ' '; }else{ result += *it; } } return result; } // 测试上述函数 int main(){ cout<<ReverseWords("the sky is blue")<<"\n"; // 输出:"blue is sky the" } ``` 这种方法特别适合处理像题目描述那样的实例,在去除首尾多余空白之后再执行相应的转换逻辑[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值