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

一、 344.反转字符串

力扣题目链接

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

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

你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

示例 1: 输入:[“h”,“e”,“l”,“l”,“o”] 输出:[“o”,“l”,“l”,“e”,“h”]

示例 2: 输入:[“H”,“a”,“n”,“n”,“a”,“h”] 输出:[“h”,“a”,“n”,“n”,“a”,“H”]

  • 题目虽然要求不能有额外的空间,但是它的测试程序倒也没有测出来我其实是开辟了额外的空间,有漏洞。

  • 数组值好像是只能覆盖不能修改,题目的要求只能有O(1)的空间复杂度,原地修改吗,想不到怎么做。
    在这里插入图片描述

  • 看了一眼视频说是可以使用双指针写法,我于是我按照自己的思路写出了双指针方法。顺利通过。这个题只需要注意反转代表首尾交换就可以了。

class Solution {
public:
    void reverseString(vector<char>& s) {
        char temp;
        int left = 0;
        int right = s.size() - 1;
        while(left < right){
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }        
    }
};

在这里插入图片描述

  • 代码随想录里直接使用了swap函数。
class Solution {
public:
    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 个字符,其余字符保持原样。

示例:

输入: s = “abcdefg”, k = 2 输出: “bacdfeg”

  • 这道题给我的感觉是模拟题,代码随想录里确实也写的是模拟。
  • 下面代码是我写的,只能通过部分测试用例,看了一下思路和代码随想录中题解的自己实现reverse函数的思路一样,可能是边界条件有错误。
  • 好家伙,下面这个说的就是我了。

一些同学可能为了处理逻辑:每隔2k个字符的前k的字符,写了一堆逻辑代码或者再搞一个计数器,来统计2k,再统计前k个字符。

  • 下面这个技巧很可以

其实在遍历字符串的过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。

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

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

class Solution {
public:
    string reverseStr_base(string s, int left, int right){
        char temp;
        while(left < right){ //下面的逻辑可以用swap代替
            temp = s[left];
            s[left] = s[right];
            s[right] = temp;
            left++;
            right--;
        }
        return s;
    }

    string reverseStr(string s, int k) {
        int size = s.size();
        int count = 0;
        int left = 0;
        int right = left + k;
        for(int i=0; i<size; i++){
            if(count == 2*k){
                s = reverseStr_base(s, left, right - 1);
                left = left + 2*k;
                right = right + 2*k;
            }
            if(size - count < 2*k){
                if(size - count < k){
                    s = reverseStr_base(s, left, size -1);
                }else if(k <= (size - count) <2*k){
                    s = reverseStr_base(s, left, left + k);
                }
            }
            count++;
        }
        
        return s;
    }
};
  • 代码随想录里用了写了三种题解,下面用库函数的reverse的代码是最简洁的。
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;
    }
};

三、剑指Offer 05.替换空格

力扣题目链接

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

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

  • 以为是简单题,就替换一下就完了,一看发现想少了。字符数就不一样,要先扩充空间。
    在这里插入图片描述
  • 看完思路,抄的代码随想录的题解,双指针yyds
  • 这个题主要分三步走:
    1.统计空格的个数
    2.扩充字符串s的大小,也就是每个空格替换成"%20"之后的大小
    3.从后先前将空格替换为"%20"
    在这里插入图片描述
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;
    }
};

四、151. 反转字符串中的单词

力扣题目链接

给定一个字符串,逐个翻转字符串中的每个单词。

示例 1: 输入: “the sky is blue” 输出: “blue is sky the”

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

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

  • 读完题感觉太复杂了,每个单词的数量不是相同还不是固定的,后面还要兼顾空格。感觉没法把反转字符串的思路迁移到这道题中来。

1.我一开始想的是反转时就解决空格问题,但应该是先不管空格先反转,反转结束之后再删除空格。这种一步一步走的想法确实应该学一学。
2.要记得reverse这个专门用来反转字符串的库函数,用起来方便。
3.移除空格使用双指针。

1.字符串复杂操作拿捏了! | LeetCode:151.翻转字符串里的单词
2.代码随想录本题题解

  • 这个题在代码随想录里有精简版本的代码,但需要吃透各种细节。
  • 直接copy的代码,真的挺复杂。
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;
    }
};

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

力扣题目链接

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

示例 1: 输入: s = “abcdefg”, k = 2 输出: “cdefgab”

示例 2: 输入: s = “lrloseumgh”, k = 6 输出: “umghlrlose”

限制: 1 <= k < s.length <= 10000

Carl:建议:题解中的解法如果没接触过的话,应该会想不到

  • 我自己倒是把这道题看得很简单,当作一道模拟题来写,但开辟新空间是必须的。而且代码肯定不少。
  • 然后看了一下代码随想录的题解,愣住,玩得花啊。下图已经很清楚地说明解题步骤。
    在这里插入图片描述
  • 在思路和部分代码的提示下解出来了,用了reverse函数,但要记得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;
    }
};

参考资料

代码随想录

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值