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

344.反转字符串

是否能用库函数?

如果题目的关键部分直接用库函数就可以解决,那建议不要使用库函数,如果库函数仅仅是解题过程中的一部分,且十分清楚内部原理的话,可以考虑使用库函数

代码

class Solution {
public:
    void reverseString(vector<char>& s) {
        int first = 0;
        int last = s.size()-1;
        for(;first<last;first++, last--){
            swap(s[first], s[last]);
        }
    }
};

541. 反转字符串II

代码

// 可以对字符串直接处理,不用转成数组
class Solution {
public:
    string reverseStr(string s, int k) {
        for(int i=0; i<s.size(); i += 2*k){
            // 剩余字符少于k,则剩余字符全部反转
            if(i+k > s.size()-1){
                reverse(s.begin()+i, s.end());
            }
            // 否则翻转前k  注意第二项填的是下一个指针,即左闭右开
            else{
                reverse(s.begin()+i, s.begin()+i+k);
            }
        }
        return s;
    }
};

本题中,reverse不是关键,所以可以直接用库,库函数输入的是地址

如果要自己写reverse,如果写成左闭右闭,那输入时要记得-1

剑指Offer 05.替换空格

思路:

很多数组填充问题,都是预先给数组扩容填充后的大小,然后从后往前进行操作,两个好处

  • 不用申请新数组
  • 从后往前填充元素,避免从前往后填充时,每次添加元素都要在添加元素后的所有元素向后移动

具体步骤:

1-求空格数

2-扩充字符串,利用resize方法,扩充大小为空格数*2

3-快指针j和慢指针i,j从原字符串末尾开始,i从新字符串末尾开始,同时向左遍历,如果j遇到空格,则i填充0 2 %;如果j不是空格,则i填充与j相同的值

代码

class Solution {
public:
    string replaceSpace(string s) {
        // 求空格个数
        int count = 0;
        // for(int i=0; i<s.size(); i++){
        //     if(s[i] == ' '){
        //         count++;
        //     }
        // }
        for(char m:s){
            if(m == ' '){
                count++;
            }
        }

        // 根据空格个数扩充为新的字符串
        int oldLen = s.size();  // 慢指针要用
        s.resize(s.size() + count*2);
        int newLen = s.size();

        // 定义快慢指针
        int slow = oldLen-1;
        int fast = newLen-1;
        while(slow < fast){
            if(s[slow] == ' '){
                s[fast] = '0';
                s[fast-1] = '2';
                s[fast-2] = '%';
                fast -= 3;
            }
            else{
                s[fast] = s[slow];
                fast--;
            }
            slow--;

        }
        return s;
    }
};

151.翻转字符串里的单词

思路

分三步走:

1-移除多余空格,只剩下单词中间的一个空格

2-进行翻转,可以利用reverse(),时间复杂度也是O(n) (因为这一步不是关键步骤,所以可以用库函数,如果要写可以参照上面第一题)

3-每个单词内进行翻转

本题重点是移除多余空格,如果使用库函数erase(),则时间复杂度为O(n^2) (因为是在遍历中erase,纯erase是O(n)),所以应用双指针法,为O(n)

双指针移除多余空格

快指针要遍历全部元素

如果快指针指向元素不为空,就要进入一个循环,让fast后面的一个单词赋给slow(即fast指向空或者到头时跳出循环)

但在单词赋值之前要判断一下,这个单词是否为第一个单词,如果不是第一个单词,就要给slow先赋空格,再从fast把单词搬过来

代码

class Solution {
public:
    // 注意自定义的两个函数传递的是地址
    // 翻转函数,要输入头和尾是方便后面单词的翻转
    void reverse(string& s, int begin, int end){
        for(int i=begin, j=end; i<j; i++, j--){
            swap(s[i], s[j]);
        }
    }
    //去除多余空格函数 双指针法
    void removeExaSpace(string& s){
        int slow = 0;
        for(int fast=0; fast < s.size(); fast++){
            if(s[fast] != ' '){
                if(slow != 0)  s[slow++] = ' ';  // 判断当赋值过来的单词不是第一个时,slow要先给个空格
                // 让一个单词整个从fast赋值到slow   while中之所以还要加这两个条件是因为上面for循环锁定具体fast,现在让这个具体的fast往后走,要设置循环跳出条件
                while(fast<s.size() && s[fast]!=' '){
                    s[slow++] = s[fast++];
                }
            }
        }
        s.resize(slow); //slow大小即去除空格后的字符串大小(正好比索引值大1)
    }

    string reverseWords(string s) {
        removeExaSpace(s);
        reverse(s, 0, s.size()-1);
        // 双指针法(自己写的)
        // for(int slow=0, fast=0; fast<s.size(); fast++){
        //     if(s[fast] == ' '){
        //         reverse(s, slow, fast-1);
        //         slow = fast+1;
        //     }
        //     if(fast == s.size()-1){
        //         reverse(s, slow, fast);
        //     }
        // }

        // 其实也是双指针法,start相当于上面的slow,i相当于fast,只是巧的是i可以等于s.size()
        int start = 0;
        for(int i=0; i<=s.size(); i++){
            if(s[i] == ' ' || i==s.size()){
                reverse(s, start, i-1);
                start = i+1;
            }
        }
        return s;
    }
};

包含三部分:翻转指定范围元素、去除多余空格(重点)、根据空格间隔翻转每个单词

在最后一部分中,巧妙的写法是i可以等于s.size(),这样把fast指向结尾的情况考虑进去,不用再分情况讨论

注意:自定义的两个函数传的是地址,不用return,而题目中给的函数是传入string类型返回string类型的,不是地址

剑指Offer58-II.左旋转字

思路:整体翻转+局部翻转的思路(实际为两个局部翻转,再整体翻转)

 代码

class Solution {
public:
    void reverse(string& s, int begin, int end){
        for(int i=begin, 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;
    }
};

总结

今日四道题都是反转问题。344 反转字符串是最简单,利用双指针;541 反转字符串Ⅱ加上条件,利用规律一段一段反转;151反转字符串中单词,先整体反转,再把每个单词反转回来(但这道题中最难的是去除多余空格-双指针法);最后一题左旋转字也是利用整体反转+局部反转

剩下一道替换空格,为先扩充数组,再利用双指针法,从后往前填充(如果从前往后会每次都让后面元素全部后移,时间复杂度O(n^2))

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值