代码随想录day7 | 344.反转字符串 541.反转字符串II 剑指Offer05.替换空格 151.翻转字符串里的单词 剑指Offer58-II.左旋转字符串

本文介绍了几种字符串处理的C++实现,包括反转字符串、分组反转、替换空格以及翻转单词。这些方法主要利用双指针技巧,确保在有限的空间复杂度内完成操作。文章特别强调了处理边界条件和特殊情况的重要性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、反转字符串

344.反转字符串

class Solution
{
public:
    void reverseString(vector<char> &s)
    {
        // 双指针
        int left = 0;
        int right = s.size() - 1;
        while (left < right) // 相等没要交换了
        {
            swap(s[left++], s[right--]);
        }
    }
};

二、反转字符串II

541.反转字符串II
读题感觉被坑了!表述的很不清楚!
它这个意思是对整个字符串s,每2k个字符中,前k个进行反转。相当于是分组反转的那个意思。
在这里插入图片描述

class Solution
{
public:
    // 自己实现的reverse是 左闭右闭 的
    // 库函数实现的reverse是 左闭右开 的
    void reverse(string &s, int left, int right)
    {
        while (left < right)
        {
            swap(s[left++], s[right--]);
        }
    }
    string reverseStr(string s, int k)
    {
        for (int i = 0; i < s.size(); i += 2 * k)
        {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. k<= 剩余字符数 < 2k,则反转前 k 个字符
            if (i + k <= s.size())
            {
                reverse(s, i, i + k - 1);
                continue; // continue用的很牛逼
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
            reverse(s, i, s.size() - 1);
        }
        return s;
    }
};

// 等价的不要continue的写法
class Solution
{
public:
    // 自己实现的reverse是 左闭右闭 的
    // 库函数实现的reverse是 左闭右开 的
    void reverse(string &s, int left, int right)
    {
        while (left < right)
        {
            swap(s[left++], s[right--]);
        }
    }
    string reverseStr(string s, int k)
    {
        for (int i = 0; i < s.size(); i += 2 * k)
        {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. k<= 剩余字符数 < 2k,则反转前 k 个字符
            if (i + k <= s.size()) // 为啥加这个判断? 为啥要取得等于 <= ?
            {
                reverse(s, i, i + k - 1);
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
            else // i+k >s.size() ,说明剩下的字符数不足k个
            {
                reverse(s, i, s.size() - 1);
            }
        }
        return s;
    }
};

1、if(i+k <= s.size()) // 为啥加这个判断? 为啥要取得等于 <= ?

因为不加这个判断的话,reverse(s,i,i+k-1),这里剩余的字符不足k个,是不能对i到i+k进行逆置的。 取等号的临界条件,其实可以画个特例图,例如 “abc”,k=3。

在这里插入图片描述

2、为啥continue呢?

因为对于尾部的不足k个处理,我们写在下面了。

三、替换空格

剑指Offer05.替换空格
不要借助额外的空间,使用O(1)的空间复杂度。
代码随想录详解
双指针解法:从后往前,替换空格,i 指向新长度的末尾,j 指向旧长度的末尾。
在这里插入图片描述
在这里插入图片描述
直到两个指针 相遇或者是i超过j ,结束!

class Solution
{
public:
    string replaceSpace(string s)
    {
        // 1.先统计空格的数量
        int countspace = 0;
        int oldsize = s.size();
        for (auto e : s)
        {
            if (e == ' ')
                countspace++;
        }
        // 2.扩容s
        s.resize(oldsize + countspace * 2);

        // 3.定义双指针 从后向前替换
        int newsize = s.size();
        int i = newsize - 1;
        int j = oldsize - 1;
        while (j < i)
        {
            // 如果i遇到空格 ,那么开始替换
            if (s[j] == ' ')
            {
                s[i] = '0';
                s[i - 1] = '2';
                s[i - 2] = '%';
                i -= 2;
            }
            else
            {
                s[i] = s[j];
            }
            i--;
            j--;
        }
        return s;
    }
};

四、[有点难]翻转字符串里的单词

151.翻转字符串里的单词

这个题比较麻烦的就是,给的字符串有前导空格,中间连续空格,结尾连续空格。
题目还要求把多余的空格给删除了。我们要用O(1)的空间复杂度。
在这里插入图片描述
大体思路:整个大字符串整体反转,然后对于每个单词再进行反转,就可以得到目标字符串了!
在这里插入图片描述

参考我写的这篇移除元素的双指针解法:我写的 27.移除元素
利用双指针移除多余空格的操作:要求不使用库函数,空间复杂度为O(1)

fast快指针:指向我们想要获取的元素
slow慢指针:指向快指针获取元素所指向的新的位置(这个位置依旧在原字符串中)

class Solution
{
public:
    void reverse(string &s, int left, int right)
    {
        // 左闭右闭的写法 [ ]
        while (left < right)
        {
            swap(s[left++], s[right--]);
        }
    }
    void removeSpace(string &s)
    {
        int fast = 0;
        int slow = 0;
        for (; fast < s.size(); fast++)
        {
            // 符合我们要收集的字母,条件:fast指向不为空
            if (s[fast] != ' ')
            {
                // 细节处理:我们还需要保留单词之间的一个空格,
                // 但是除了首个单词,我们是直接放在slow指向的位置的
                if (slow != 0) // 特判一下,当slow不是指向第一个起始位置的时候
                {
                    s[slow++] = ' '; // slow慢指针所指向的位置,手动添加一个单词间的空格
                }
                
                // 正常的将fast指向的值赋值给慢指针指向的值 
                // 需要持续赋值使用while
                while (fast < s.size() && s[fast] != ' ')
                {
                    s[slow++] = s[fast++];
                }
            }
        }
        s.resize(slow);
    }
    string reverseWords(string s)
    {
        removeSpace(s);
        reverse(s, 0, s.size() - 1);
        int start = 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.左旋转字符串

string substr(size_t pos = 0, size_t len = npos) const;
// 我第一眼想到的解法
class Solution
{
public:
    string reverseLeftWords(string s, int k)
    {
        // 可以分为两次子串截取,然后再将两个子串拼接起来
        return s.substr(k, s.size() - k) + s.substr(0, k);
    }
};

// 多次逆置也可以
// 先逆置前部分,后逆置后部分,最后整体逆置即可
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;
    }
};

第一种解法使用了 substr 函数进行子串截取,但 substr 函数返回的是一个新的 std::string 对象,需要使用额外的内存来存储生成的子串。然而,在 C++ 中,std::string 的拷贝操作会隐式地调用复制构造函数,因此,原字符串和子串共享底层的字符数组,并不会额外占用内存空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值