代码随想录算法训练营Day8|344.反转字符串、541. 反转字符串II、替换数字、151.翻转字符串里的单词、右旋字符串

 

目录

344.反转字符串

 前言:

方法一:调用库函数

方法二:自己构造

 第一种:一个变量实现

第二种:两个变量实现

541. 反转字符串II

前言: 

方法一:双指针法

方法二:单指针实现

替换数字

 前言:

算法实现:

算法分析:

151.翻转字符串里的单词

 前言:

思路分析:

算法实现:

算法分析:

右旋字符串

 前言:

算法实现:

手搓reverse函数版

调用库函数版

算法对比:

总结


344.反转字符串

题目链接

文章链接

 前言:

        本题要反转一个字符串,可以直接利用库函数reverse实现,自己构造也很轻松。

方法一:调用库函数

class Solution {
public:
    void reverseString(vector<char>& s) {
        reverse(s.begin(), s.end());
    }
};

        直接reverse库函数实现,没什么好说的。

方法二:自己构造

 第一种:一个变量实现
class Solution {
public:
    void reverseString(vector<char>& s) {
        int len = s.size();
        for (int i = 0; i < len / 2; i++){
            swap(s[i], s[len - 1 - i]);
        }
    }
};
第二种:两个变量实现
class Solution {
public:
    void reverseString(vector<char>& s) {
        int len = s.size();
        for (int i = 0, j = len - 1; i < j; i++, j--){
            swap(s[i], s[j]);
        }
    }
};

        实现思路都很简单,不过多介绍。

541. 反转字符串II

题目链接

文章链接

前言: 

        本题也是反转字符串,只是要按照题目所给的条件反转符合条件的部分字符串,需要进行一些具体的判断。

方法一:双指针法

class Solution {
public:
    string reverseStr(string s, int k) {
        int len = s.size();
        int i = 0, j = 0; //定义快慢指针
        for (j = 0; j < len; j++){
            if (j - i == 2 * k && j < len){ // 当快指针移动到距离慢指针2k个单位长度且未达到数组末尾时
                reverse(s.begin() + i, s.begin() + i + k);
                i = j;
            } 
            if (j == len - 1 && j - i < 2 * k && j - i >= k){ // 快指针到达末尾时与慢指针的距离在k与2k之间时
                reverse(s.begin() + i, s.begin() + i + k);
            }
            if (j == len - 1 && j - i < k){ // 快指针到达末尾时与慢指针的距离小于k时
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

        让快指针先向后移动,在未到达字符串末尾时 ,每当快指针与慢指针距离2k时,反转慢指针起往后k个字符,再更新慢指针的位置,使之与快指针重合后再继续移动快指针。当快指针到达字符串末尾时,再讨论慢指针与快指针的距离进行具体的翻转方法。

方法二:单指针实现

class Solution {
public:
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)){
            if (i + k <= s.size()) reverse(s.begin() + i, s.begin() + i + k);
            else
                reverse(s.begin() + i, s.end());
        }
        return s;
    }
};

        让i以2k的大小遍历字符串,当i距离串尾的距离大于k时,可以直接反转从i起始的k个字符。若不满足条件,说明i此时后面的字符数小于k,反转后面的所有字符。

替换数字

题目链接

文章链接

 前言:

        本题要求将字符串中的数字转化为字符串"number",若能够使用额外的辅助空间,本题实现并不难,直接将原字符串中的字符依次往辅助空间中添加,遇到数字时,改为添加"number"字符串,最后返回辅助空间即可。若只能在原字符串中实现替换,则就显得略微麻烦些。

算法实现:

#include <iostream>
using namespace std;
 
int main(){
     
    string s;
    cin >> s;
    int len = s.size();
    int count = 0;
    for (int i = 0; i < len; i++){
        if (s[i] >= '0' && s[i] <= '9') count++;
    }
    s.resize(len + 5 * count);
    int new_len = s.size();
    for (int i = len - 1, j = new_len - 1; i < j; i--, j--){ //循环终止条件也可以是i >= 0
        if (s[i] < '0' || s[i] > '9'){
            s[j] = s[i];
        }
        else{
        s[j] = 'r';
        s[j - 1] = 'e';
        s[j - 2] = 'b';
        s[j - 3] = 'm';
        s[j - 4] = 'u';
        s[j - 5] = 'n';
        j -= 5;
    }
    }
     
    cout << s << endl;
     
    system("pause");
    return 0;
}

算法分析:

        本题在字符串字符顺序的处理上需要注意,若从前往后遍历,遇到数字进行字符串替换,则需要将后面的所有字符都进行向后移动,本算法采用从后向前遍历的处理方式(事实上大多数数组和字符串的元素替换为了减少时间复杂度都采取从后往前的方式)。

        先统计出字符串中数字的个数,进行字符串的扩容,并利用双指针的思想,一个指针指向原字符串的末尾,另一个指针则指向扩容后新字符串的末尾,当第一个指针所指位置不为数字时,将该位置的值填充给新字符串指针所指位置,两指针再同时继续向前移动;若第一个指针所指位置为数字时,新字符串则依次逆序填充"number"字符串。最终当两指针重合时,填充结束。

151.翻转字符串里的单词

题目链接 

文章链接

 前言:

        本题要翻转字符串中的单词,并且将开头末尾的空格删除,单词之间有且仅有一个空格。

思路分析:

        先对字符串中的多余空格删去(难点),再对整个字符串进行翻转,翻转后以单词间的空格为判断条件,逐个反转每个单词,即可实现题目要求。

算法实现:

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 removeExtraSpace(string& s){
        int slow = 0;
        for (int fast = 0; fast < s.size(); fast++){
            if (s[fast] != ' '){ //fast指针跳过了前面的空格
                if (slow != 0) s[slow++] = ' '; // 若slow不为开头要在单词之间留一个空格
                while (fast < s.size() && s[fast] != ' '){
                    s[slow++] = s[fast++];
                }
            }
        }
        s.resize(slow);
    }

    string reverseWords(string s) {
        removeExtraSpace(s);
        reverse(s, 0, s.size() - 1);
        int i = 0;
        for (int j = 0; j <= s.size(); j++){
            if (s[j]  == ' ' || j == s.size()){
                reverse(s, i, j - 1);
                i = j + 1;
            }
        }
        return s;
    }
};

算法分析:

        该算法中自定义了两个函数:反转函数reverse和删除多余空格函数removeExtraSpace,本算法的核心是removeExtraSpace函数的实现,也是依靠双指针进行实现,让快指针先进行遍历,当快指针指向非空格时,对slow指针进行判断,若slow还在初始位置(0),则直接将fast指向的字符赋给slow指向的位置;若slow已经不在初始位置(则在两个单词之间),要先给slow指向的字符位置赋一个空格再进行元素的填充。

        最后在reverseWords函数内删除多余空格和反转字符串后,对于每个单词的翻转也是利用双指针法进行实现。

右旋字符串

题目链接

文章链接

 前言:

        本题的算法实现承接上题的思路:先翻转整体字符串,再翻转字符串的各部分。

算法实现:

手搓reverse函数版
#include <iostream>
using namespace std;
 
void reverse(string& s, int begin, int end){
    for (int i = begin, j = end; i < j; i++, j--){
        swap(s[i], s[j]);
    }
}
 
int main(){
    int count;
    cin >> count;
    string s;
    cin >> s;
    reverse(s, 0, s.size() - 1);
    reverse(s, 0 , count - 1);
    reverse(s, count, s.size() - 1);
    cout << s << endl;
     
    system("pause");
    return 0;
}
调用库函数版
#include <iostream>
using namespace std;
#include <algorithm>
 
int main(){
    int count;
    cin >> count;
    string s;
    cin >> s;
    reverse(s.begin(), s.end());
    reverse(s.begin(), s.begin() + count);
    reverse(s.begin() + count, s.end());
    cout << s << endl;
     
    system("pause");
    return 0;
}

算法对比:

        自定义reverse函数时,我们传入的变量是字符串s的引用(不用再返回字符串且效率更高),反转字符串起始位置begin和终止位置end,是一个左闭右闭的区间;而在调用库函数reverse时,是一个左闭右开的区间,因为第二个传入的变量是终止位置的下一个位置。

        同时,在引用库函数reverse的时候要调用#include <algorithm> 头文件。

总结

        今天学习了很多关于字符串操作的技巧和知识,受益匪浅,明天继续学习字符串,坚持就是胜利!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值