【算法-字符串2】替换空格 + 反转单词

今天,带来字符串相关算法的讲解。文中不足错漏之处望请斧正!

理论基础点这里


1. 替换空格

题目描述:请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
来源:力扣(LeetCode)
难度:简单
提示:
0 <= s 的长度 <= 10000
示例 1:
输入:s = “We are happy.” 输出:“We%20are%20happy.”

题意转化

把字符串内的所有' '替换为%20.

解决思路(抽象)

使用额外空间

创建一个空字符串, 遍历给定字符串s, 遇到字符的时候直接插入空字符串, 遇到空格的时候插入%20.

不使用额外空间

不用额外空间就要扩容:统计空格个数,每个空格替换成“%20”,就意味着每个空格都需要额外两个字符的空间。

扩容后,从后向前替换

为什么是从后向前?因为从前向后替换数组元素,每次替换完都要把后面所有元素往后移动,这就是O(n^2)的复杂度了。

编程实现(具体)

使用额外空间

class Solution {
public:
    string replaceSpace(string s) {
        string ret;
        for(char &ch : s) {
            if(ch == ' ') ret += "%20";
            else ret += ch;
        }
        return ret;
    }
};

不使用额外空间

class Solution {
public:
    string replaceSpace(string s) {
				// 扩容
        int count = 0; //空格个数
        for(char &ch : s) if(ch == ' ') ++count;
        int oldSize = s.size();
        s.resize(s.size() + count * 2);
        int newSize = s.size();
        
        // 从后向前替换
        for(int i = oldSize - 1, j = newSize - 1; i < j; --i, --j) {
            if(s[i] != ' ') {
                s[j] = s[i];
            } else {
                s[j] = '0';
                s[j - 1] = '2';
                s[j - 2] = '%';
                j -= 2; //多了两次操作,j指针对应移动
            }
        }
        return s;
    }
};

2. 反转单词

给你一个字符串 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 中 至少存在一个 单词

进阶:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。

题意转化

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

解决思路(抽象)

首先, 要保证单词之间用单个空格连接(头尾没有空格).

原地处理的话, 可以使用双指针重新填充字符串:

  • fast:遍历s拿字符(遇到空格就跳过)
  • slow:从头填充合法字符串

简单说, 每次遇到空格就自己把单词填充上, 再手动添加一个空格, 就可以满足题目要求.

单词顺序颠倒, 我们无法直接实现, 但我们能做什么?

  • 颠倒字符串顺序
  • 颠倒单个单词顺序

前者就可以颠倒单词顺序, 只不过会让单个单词也颠倒, 我们再次颠倒每个单词, 不就得到了题目要求的字符串了吗.
比如 如 hello world , 整体反转后是 dlrow olleh;然后对单词反转 world hello .
简单说, 先总体反转, 再局部反转.

编程实现(具体)

最后一个单词, 可以在反转单词内部顺序的循环中处理, 也可以跳出循环单独处理.

最后一个单词反转单词内部顺序的循环中处理:

class Solution {
public:
    string reverseWords(string s) {
        //双指针重新填充字符串
        int slow; // 填充字符串
        int fast; // 遍历s
        for (slow = 0, fast = 0; fast < s.size(); ++fast) {
            if (s[fast] == ' ') continue; // 如果是空格就跳过

            // 填充当前单词
            while (fast < s.size() && s[fast] != ' ') s[slow++] = s[fast++];

            // 填充空格(只有最后一个单词后不需要填充)
            s[slow++] = ' ';
                
        }
        s.resize(slow - 1); // 去掉多余的空格(最后一个单词后不需要填充, 但我们默认填充了)

        cout << "处理后的字符串: " << s << endl;

        // 整体反转
        reverse(s.begin(), s.end());
        // 局部反转
        int wordBegin = 0;
        int wordEnd= 0;
        while (wordEnd <= s.size()) {
            if (s[wordEnd] == ' ' || wordEnd == s.size()) {
                reverse(s.begin() + wordBegin, s.begin() +wordEnd);
                wordBegin = wordEnd + 1;
                wordEnd = wordBegin;
            }
            ++wordEnd;
        }
        
        return s;
    }
};

最后一个单词跳出循环单独处理:

class Solution {
public:
    string reverseWords(string s) {
        //双指针重新填充字符串
        int slow; // 填充字符串
        int fast; // 遍历s
        for (slow = 0, fast = 0; fast < s.size(); ++fast) {
            if (s[fast] == ' ') continue; // 如果是空格就跳过

            // 填充当前单词
            while (fast < s.size() && s[fast] != ' ') s[slow++] = s[fast++];

            // 填充空格(只有最后一个单词后不需要填充)
            s[slow++] = ' ';
                
        }
        s.resize(slow - 1); // 去掉多余的空格(最后一个单词后不需要填充, 但我们默认填充了)

        cout << "处理后的字符串: " << s << endl;

        // 整体反转
        reverse(s.begin(), s.end());
        // 局部反转
        int wordBegin = 0;
        int wordEnd= 0;
        while (wordEnd < s.size()) {
            if (s[wordEnd] == ' ') {
                reverse(s.begin() + wordBegin, s.begin() +wordEnd);
                wordBegin = wordEnd + 1;
                wordEnd = wordBegin;
            }
            ++wordEnd;
        }
        reverse(s.begin() + wordBegin, s.begin() +wordEnd); // 反转最后一个单词
        
        return s;
    }
};

今天的分享就到这里了,感谢您能看到这里。

这里是培根的blog,期待与你共同进步!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

周杰偷奶茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值