day8 | 344.反转字符串、 541. 反转字符串II、剑指Offer 05.替换空格、151.翻转字符串里的单词、剑指Offer58-II.左旋转字符串

目录:

链接

题目链接:

https://leetcode.cn/problems/reverse-string/

https://leetcode.cn/problems/reverse-string-ii/

https://leetcode.cn/problems/ti-huan-kong-ge-lcof/

https://leetcode.cn/problems/reverse-words-in-a-string/

https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/

解题及思路学习

344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须**原地修改输入数组**、使用 O(1) 的额外空间解决这一问题。

示例 1:

输入:s = ["h","e","l","l","o"]
输出:["o","l","l","e","h"]

思考:反转一下字符串。可以用双指针,以数组中间为界限。一个指针从左往右遍历,一个指针从右往左遍历,利用临时中间值,交换两个值。

class Solution {
public:
    void reverseString(vector<char>& s) {
        int left = 0;
        int right = s.size()-1;
        char tmp;

        while (left < right) {
            tmp = s[left];
            s[left] = s[right];
            s[right] = tmp;
            left++;
            right--;
        }
    }
};

随想录:思路差不多。不同之处在于,使用了库函数swap。

void reverseString(vector<char>& s) {
    for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
        swap(s[i],s[j]);
    }
}

这个库函数还挺常用的。

如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数。

如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数。

541. 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

示例 1:

输入:s = "abcdefg", k = 2
输出:"bacdfeg"

思考:想到了滑动窗口,只需要考虑0-k区间内的字符进行反转,之后这个窗口往右移2k个单位。移动之后判断一下窗口里面的字符个数,并对不同区间进行不同操作。

随想录:思路差不多,不过比我想得要简单。

因为要找的也就是每2 * k 区间的起点,这样写,程序会高效很多。

所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。

(这代码好简洁,谁看了能不爱?)

class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s.begin() + i, s.begin() + i + k );
            } else {
                // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

不适用库函数reverse,自己实现:

class Solution {
public:
    void reverse(string& s, int strat, int end) {
        for (int i = strat, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            if (i + k <= s.size()){
                reverse(s, i, i + k -1);
                continue;
            } else {
                reverse(s, i, s.size() -1);
            }
        }
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)或O(n), 取决于使用的语言中字符串是否可以修改

另一种思路解法(思路差不多,不过是换成while的形式)

class Solution {
public:
    string reverseStr(string s, int k) {
        int n = s.size(),pos = 0;
        while(pos < n){
            //剩余字符串大于等于k的情况
            if(pos + k < n) reverse(s.begin() + pos, s.begin() + pos + k);
            //剩余字符串不足k的情况 
            else reverse(s.begin() + pos,s.end());
            pos += 2 * k;
        }
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1)

reverse函数学习:https://blog.csdn.net/weixin_46024428/article/details/116094154

reverse是C++下的库函数,可用于翻转字符串,翻转链表等。使用需要添加头文件**#include **

reverse()会将区间[beg,end)内的元素全部逆序;

1.reverse(str.begin(),str.end()) 反转字符串
 	 
2.reverse(vector.begin(),vector.end()) 反转向量
 
3.reverse(a,a+strlen(a)) 反转数组

reverse()  全部反转,无返回值

注意传入的直接是字符串、向量和数组的开头、结尾。

剑指Offer 05.替换空格

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。

示例 1: 输入:s = “We are happy.”
输出:"We%20are%20happy.

思考:直接for循环遍历整个数组,如果是空格,就将其替换。

class Solution {
public:
    string replaceSpace(string s) {
        int count = 0;
        int oldsize = s.size();
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == ' ') {
                count++;
            }
        }
        int newsize = s.size() + (count * 2) ;
        s.resize(newsize);
        oldsize--;
        newsize--;
        while (oldsize <  newsize){
            if (s[oldsize] == ' ') {
                s[newsize--] = '0';
                s[newsize--] = '2';
                s[newsize--] = '%';
                oldsize--;
            } else {
                s[newsize--] = s[oldsize--];
            }
        }
        return s;
    }
};

随想录:不仅要考虑替换,还要注意替换之后的空间问题。用双指针的思想,先统计空格个数,并扩充字符串长度,之后利用双指针进行替换。

其实很多数组填充类的问题,都可以先预先给数组扩容带填充后的大小,然后在从后向前进行操作。

这么做有两个好处:

  1. 不用申请新数组。
  2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。

从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素向后移动。

class Solution {
public:
    string replaceSpace(string s) {
        int count = 0; // 统计空格的个数
        int sOldSize = s.size();
        for (int i = 0; i < s.size(); i++) {
            if (s[i] == ' ') {
                count++;
            }
        }
        // 扩充字符串s的大小,也就是每个空格替换成"%20"之后的大小
        s.resize(s.size() + count * 2);
        int sNewSize = s.size();
        // 从后先前将空格替换为"%20"
        for (int i = sNewSize - 1, j = sOldSize - 1; j < i; i--, j--) {
            if (s[j] != ' ') {
                s[i] = s[j];
            } else {
                s[i] = '0';
                s[i - 1] = '2';
                s[i - 2] = '%';
                i -= 2;
            }
        }
        return s;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(1)

s.resize() 扩充字符串大小。我的是另一种写法,用while来判断。

151. 反转字符串中的单词

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

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

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

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

示例 1:

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

思考:两个指针分别从两端到中间遍历,从第一个不为空格的字符算起,知道遇到下一个空格,将中间作为一个整体。分别对调两端的分字符串。

随想录:提高一下本题的难度:**不要使用辅助空间,空间复杂度要求为O(1)。**将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。

所以解题思路如下:

  • 移除多余空格
  • 将整个字符串反转
  • 将每个单词反转
class Solution {
public:
    void reverse(string& s, int start, int end){ //翻转,区间写法:左闭右闭 []
        for (int i = start, j = end; i < j; i++, j--) {
            swap(s[i], s[j]);
        }
    }

    void removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
        int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.html
        for (int i = 0; i < s.size(); ++i) { //
            if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
                if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow); //slow的大小即为去除多余空格后的大小。
    }

    string reverseWords(string s) {
        removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
        reverse(s, 0, s.size() - 1);
        int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是0。
        for (int i = 0; i <= s.size(); ++i) {
            if (i == s.size() || s[i] == ' ') { //到达空格或者串尾,说明一个单词结束。进行翻转。
                reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转。
                start = i + 1; //更新下一个单词的开始下标start
            }
        }
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度: O(1) 或 O(n)

在解题的时候,可以考虑时候为一些功能写一些函数。主要是思路得对。

剑指Offer58-II.左旋转字符串

字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。

示例 1:

输入: s = "abcdefg", k = 2
输出:"cdefgab"

思考:将字符串的前k个字符按照原来的顺序移到尾部。比较直接的一个思路,利用一个新的字符串来保存前k个字符,并将s整体前移k个单位,之后将保存的字符串移到尾部。

随想录:提升一下本题难度:不能申请额外空间,只能在本串上操作

依然可以通过局部反转+整体反转 达到左旋转的目的。(🐂)

具体步骤为:

  1. 反转区间为前n的子串
  2. 反转区间为n到末尾的子串
  3. 反转整个字符串

最后就可以达到左旋n的目的,而不用定义新的字符串,完全在本串上操作。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9DyyAVE3-1685781479322)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/fb608c93-b024-4722-8ea7-cadd3a1080e7/Untitled.png)]

自己定义reverse函数的写法:

class Solution {
public:
    void reverse(string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--){
            swap(s[i], s[j]);
        }
    }
    string reverseLeftWords(string s, int n) {
        reverse(s, 0, n-1);
        reverse(s, n, s.size()-1);
        reverse(s, 0, s.size()-1);
        return s;

    }
};

更简单版本,直接调用库函数的reverse函数:

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        reverse(s.begin(), s.begin() + n);
        reverse(s.begin() + n, s.end());
        reverse(s.begin(), s.end());
        return s;
    }
};
  • 时间复杂度: O(n)
  • 空间复杂度:O(1)

困难及收获

困难

不是很熟练,数据结构要赶快补起来

今日收获

字符串的reverse函数和一些解题思路。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值