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

文章介绍了如何使用双指针方法反转字符串,对比了字符串与链表反转的差异,并提供了反转字符串、替换空格、去除冗余空格等操作的解决方案,强调了在处理数组和字符串时的效率考虑,包括时间复杂度和空间优化策略。

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

字符串

344. 反转字符串

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

class Solution {
public:
    string reverseStr(string s, int k) {
        for(int i = 0; i < s.size(); i += (2 * k)){
            //1.反转k个字符
            if(i + k <= s.size()){
                reverse(s.begin() + i, s.begin() + i + k);
            }

            //2.剩余的全部反转
            if(i + k > s.size()){
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

这道题目其实也是模拟,实现题目中规定的反转规则就可以了。

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

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

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

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

剑指 Offer 05. 替换空格

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++;
            }
        }

        //更新长度
        s.resize(s.size() + 2 * count);
        int newSize = s.size();

        //填充
        //两个指针同时从后向前遍历
        //保留新旧s的长度
        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;
            }
        }
        return s;
    }
};

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

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

数组、字符串 差别

这里也给大家拓展一下字符串和数组有什么差别,

字符串是若干字符组成的有限序列,也可以理解为是一个字符数组,但是很多语言对字符串做了特殊的规定,接下来我来说一说C/C++中的字符串。

在C语言中,把一个字符串存入一个数组时,也把结束符 '\0’存入数组,并以此作为该字符串是否结束的标志。

例如这段代码:

char a[5] = "asd";
for (int i = 0; a[i] != '\0'; i++) {
}

1
2
3

在C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用’\0’来判断是否结束。

例如这段代码:

string a = "asd";
for (int i = 0; i < a.size(); i++) {
}

1
2
3

那么vector< char > 和 string 又有什么区别呢?

其实在基本操作上没有区别,但是 string提供更多的字符串处理的相关接口,例如string 重载了+,而vector却没有。

所以想处理字符串,我们还是会定义一个string类型。

151. 反转字符串中的单词

erase操作的时间复杂度

逻辑很简单,从前向后遍历,遇到空格了就erase。

如果不仔细琢磨一下erase的时间复杂度,还以为以上的代码是O(n)的时间复杂度呢。

想一下真正的时间复杂度是多少,一个erase本来就是O(n)的操作。

erase操作上面还套了一个for循环,那么以上代码移除冗余空格的代码时间复杂度为O(n^2)。

那么使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。

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 eraseExtraspace(string &s){
        int slow = 0;

        //跳过思维,fast遇到非空格处理,即跳过空格
        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++;
                }
            }
        }

        //通过resize截断
        s.resize(slow);
    }

    string reverseWords(string s) {
        eraseExtraspace(s);

        reverse(s, 0, s.size() - 1);

        //去掉空格后一定从start开始
        int start = 0;
        //将每个单词反转
        for(int i = 0; i <= s.size(); i++){
            //i指向空格作为标志
            if(i == s.size() || s[i] == ' '){
                reverse(s, start, i - 1);
                //更新start 反转下一个
                start = i + 1; 
            }
        }
        return s;

    }
};

剑指 Offer 58 - II. 左旋转字符串

思路 如何实现左旋

字符串反转的一个思路:先反转局部,再反转整体

具体步骤为:

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

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

例如 :示例1中 输入:字符串abcdefg,n=2

如图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oIKWjKJR-1687750774141)(https://code-thinking.cdn.bcebos.com/pics/%E5%89%91%E6%8C%87Offer58-II.%E5%B7%A6%E6%97%8B%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2.png)]

最终得到左旋2个单元的字符串:cdefgab

思路明确之后,那么代码实现就很简单了

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;
    }
};
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值