代码随想录算法训练营第8天 | ● 344.反转字符串 ● 541. 反转字符串II ● 剑指Offer 05.替换空格 ● 151.翻转字符串里的单词 ● 剑指Offer58-II.左旋转字符串


2023年3月8日

8 第四章 字符串

今日任务

● 344.反转字符串

● 541. 反转字符串II

● 剑指Offer 05.替换空格

● 151.翻转字符串里的单词

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

详细布置


344.反转字符串

【链接】(文章,视频,题目)

建议: 本题是字符串基础题目,就是考察 reverse 函数的实现,同时也明确一下 平时刷题什么时候用 库函数,什么时候 不用库函数

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】双指针,直接干

【看后想法】swap用法

【实现困难】无

【自写代码】

class Solution {
public:
    void reverseString(vector<char>& s) {
        // 双指针
        for (int left = 0, right = s.size() - 1; left < right; left++, right--) {
            std::swap(s[left], s[right]);
        }
    }
};

【收获与时长】


541. 反转字符串II

【链接】(文章,视频,题目)

建议:本题又进阶了,自己先去独立做一做,然后在看题解,对代码技巧会有很深的体会。

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】暴力规则

【看后想法】

  • 规则可以简化,只要>=k,就将前k个翻转;只要小于k,剩余全部翻转

  • 可以直接使用std::reverse函数

    • std::reverse(s.begin()+i, s.begin()+i+k)); // 注意左闭右开。下标是i~i+k-1的k个元素被翻转了。

    • 同理,std::reverse(s.begin(), s.end())等价于std::reverse(s.begin(), s.begin() + s.size()),翻转了下标为0, size-1的size个元素,也就是所有元素。

【实现困难】

【自写代码】

class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i+=(2*k)) {
            // 如果剩余字符大于或等于 k 个,则反转前 k 个字符
            if (s.size() - i > k) {
                std::reverse(s.begin() + i, s.begin() + i + k);
            } else { // 如果剩余字符少于 k 个,则将剩余字符全部反转。
                std::reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

【收获与时长】半小时


【链接】(文章,视频,题目)

剑指Offer 05.替换空格

建议:对于线性数据结构,填充或者删除,后序处理会高效的多。好好体会一下。

题目链接/文章讲解:代码随想录

【第一想法与实现(困难)】暴力遍历,erase, 反序insert

【看后想法】

  • 暴力的方法时间复杂度较高,每次erase, insert都会有O(n)时间复杂度,因为全部后续元素都要重新排列。总体时间复杂度是O(nm),相当于平方时间

  • 如果要避免平方时间,可以用双指针方法,从后往前重新赋值。只要在新列表做一次后序的重新赋值即可

【实现困难】

【自写代码】

class Solution {
public:
    string replaceSpace(string s) {
        // 双指针,后序遍历
        // 左指针,旧字符串位置,判断是否空格。右指针,新字符串位置
        // 先找出空格数目
        int count = 0;
        int old_size = s.size();
        for (int i = 0; i < old_size; i++) {
            if (s[i] == ' ') {
                count++;
            }
        }
        s.resize(s.size() + 2 * count);
        int new_size = s.size();
        for (int left = old_size - 1, right = new_size - 1; left < right; left--, right--) {
            if (s[left] != ' ') {
                s[right] = s[left];
            } else {
                s[right - 2] = '%';
                s[right - 1] = '2';
                s[right] = '0';
                right-=2;
            }
        }
        return s;
    }
};

【收获与时长】半小时


151.翻转字符串里的单词

【链接】(文章,视频,题目)

建议:这道题目基本把 刚刚做过的字符串操作 都覆盖了,不过就算知道解题思路,本题代码并不容易写,要多练一练。

题目链接/文章讲解/视频讲解:代码随想录

【第一想法与实现(困难)】

  • 暴力,从后往前找单词,拼接到字符串中

  • 无法做到原地修改,空间复杂度不行

  • 很多细节需要处理

【看后想法】

  • 两件事情分开做,代码会比较清晰

    • 原地去除多余字符串

    • 反转单词 = 反转字符串再将单词正回来

【实现困难】

  • 原地删除多余空格,最后指针位置是空格或者串尾slow = max index + 1,而max index = size - 1,所以resize(slow)。size = slow;

  • 反转字符串中间部分,注意左闭右闭,左边++,右边–

  • 在完美空格的字符串中找单词,i是一个分割符号,空格或者串尾i = size

【自写代码】

class Solution {
public:
    string reverseWords(string s) {
        // 先原地删除多余空格(参考原地删除数组元素的快慢指针)
        // 再反转字符串,回正单词
        std::cout << "s:" << s << std::endl;
        removeExtraSpaces(s);
        std::cout << "removeExtraSpaces, s:" << s << std::endl;
        std::reverse(s.begin(), s.end());
        int start = 0;
        for (int i = 0; i <= s.size(); i++) {
            if (i == s.size() || s[i] == ' ') { // 串尾或者空格,i是分隔符
                reverseBetweenIndex(s, start, i - 1); // 左闭右闭
                start = i + 1;
            }
        }
        return s;
    }

    // 原地删除多余空格
    void removeExtraSpaces(string &s) {
        // int fast = 0; // 快指针,原来字符串下标,长,未删除
        int slow = 0; // 慢指针,新字符串下标,段,已删除
        for (int fast = 0; fast < s.size(); fast++) {
            if (s[fast] != ' ') { // 非空格才工作,删除所有空格
                if (slow != 0) { // 非第一个单词,补充空格
                    s[slow++] = ' ';
                }
                // 复制单词,直到空格或者串尾
                while (fast < s.size() && s[fast] != ' ') {
                    s[slow++] = s[fast++];
                }
            }
        }
        // 最后,slow与fast对应原字符串的空格或串尾,也就是slow = max_index + 1
        s.resize(slow);
    }
    // 左闭右闭,反转字符串中间部分
    void reverseBetweenIndex(string& s, int start, int end) {
        for (int i = start, j = end; i < j; i++, j--) {
            std::swap(s[i], s[j]);
        }
    }
};

【收获与时长】2小时,综合,比较有难度,适合后续再练手!


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

【链接】(文章,视频,题目)

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

题目链接/文章讲解:代码随想录

【第一想法与实现(困难)】直接申请新的字符串,暴力先放后半段,再放前半段

【看后想法】

  • 字符串中不使用额外空间的做法,优先考虑反转子串

【实现困难】三个reverse,没有的难度,关键是思路

【自写代码】

class Solution {
public:
    string reverseLeftWords(string s, int n) {
        // 不使用额外空间。考虑子串反转
        std::reverse(s.begin(), s.begin() + n);
        std::reverse(s.begin() + n, s.end());
        std::reverse(s.begin(), s.end());
        return s;
    }
};

【收获与时长】

20分钟,反转子串有点意思。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值