代码随想录训练营第八天|344.反转字符串、541.反转字符串II、卡码网:54.替换数字、151.反转字符串里的单词、卡码网:55.右旋转字符串

反转字符串

不考虑使用vector自带的reverse,字符串可以按数组方式进行操作,创建两个指针left和right指向头字母和尾字母,在while(right>left)循环体中,交换s[left]和s[right],并更新left和right,left++,right--。算法时间复杂度为O(n),空间复杂度为O(1)。

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

反转字符串II

题目不难,逻辑比较绕,但仔细读完加debug还是能做出来的,这里新创建了一个反转函数reverse,他需要传入字符串s的引用,翻转的数组左下标left和右下标right,反转函数的书写和上题相似。之后编写reverseStr,考虑有3种情况,若剩余长度包含2k时,转换前k个字符,当剩余长度不足2k个字符长度时,分两种情况,不足长度k,剩余长度全部反转,不足长度2k,反转k。代码如下。算法空间复杂度O(1),时间复杂度O(n)。

class Solution {
public:
    void reverse(string &s,int left,int right){
        while(right>left){
            swap(s[left],s[right]);
            left++;right--;
        }
    }
    string reverseStr(string s, int k) {
        int length = s.size();
        int begin = 0;
        while(length/(2*k)){//当length内仍存在2k个字符时
            reverse(s,begin,begin+k-1);
            length = length - 2*k;
            begin += 2*k;//注意begin的更新
        }
        if(length<k){//当剩余长度不足k时
            reverse(s,begin,s.size()-1);
        }
        else if(length<2*k){//当剩余长度大于等于k小于2k时
            reverse(s,begin,begin+k-1);
        }
        return s;

    }
};

代码随想录里是用for循环写的,我认为更简洁明了,代码贴在下面。

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]);
        }
    }
    string reverseStr(string s, int k) {
        for (int i = 0; i < s.size(); i += (2 * k)) {
            // 1. 每隔 2k 个字符的前 k 个字符进行反转
            // 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
            if (i + k <= s.size()) {
                reverse(s, i, i + k - 1);
                continue;
            }
            // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
            reverse(s, i, s.size() - 1);
        }
        return s;
    }
};

while还能写的更加简练,整合逻辑,如下所示。

class Solution {
public:
    string reverseStr(string s, int k) {
        int n = s.size(),pos = 0;
        while(pos < n){
            //剩余字符串大于等于k的情况
            if(pos + k < n) reverse(s.begin() + pos, s.begin() + pos + k);
            //剩余字符串不足k的情况 
            else reverse(s.begin() + pos,s.end());
            pos += 2 * k;
        }
        return s;
    }
};

替换数字

直观思路

新创建一个字符串ans,对输入的字符串s[i]逐个进行比较,当s[i]>='a',即当前字符属于字母,ans+=s[i],否则,s[i]为数字,ans+=“number”;

#include <iostream>
#include <string>
using namespace std;
int main() {
    string s;//创建s
    cin>>s;//通过键盘输入s
    string ans;
    int length = s.size();
    for(int i = 0; i <length;i++){
        if(char(s[i])>='a'){
            ans+=s[i];
        }
        else{
            ans+="number";
        }
    }//判断
    cout<<ans;
}

算法时间复杂度O(n),空间复杂度O(1)。

双指针法

        将输入的s根据其中数字的数量,将其扩容,left指向原数组的最后一位,并向前遍历,right指向新数组的最后一位,并向前添加元素。当left指向索引元素为数字,则在新数组中反向加入‘r’,'e','b','m','u','n',没加入一个元素,right--,否则在新数组中加入left指向索引的字母。

#include <iostream>
using namespace std;
int main() {
    string s;
    while (cin >> s) {
        int sOldIndex = s.size() - 1;
        int count = 0; // 统计数字的个数
        for (int i = 0; i < s.size(); i++) {
            if (s[i] >= '0' && s[i] <= '9') {
                count++;
            }
        }
        // 扩充字符串s的大小,也就是将每个数字替换成"number"之后的大小
        s.resize(s.size() + count * 5);
        int sNewIndex = s.size() - 1;
        // 从后往前将数字替换为"number"
        while (sOldIndex >= 0) {
            if (s[sOldIndex] >= '0' && s[sOldIndex] <= '9') {
                s[sNewIndex--] = 'r';
                s[sNewIndex--] = 'e';
                s[sNewIndex--] = 'b';
                s[sNewIndex--] = 'm';
                s[sNewIndex--] = 'u';
                s[sNewIndex--] = 'n';
            } else {
                s[sNewIndex--] = s[sOldIndex];
            }
            sOldIndex--;
        }
        cout << s << endl;       
    }
}

反转字符串里的单词

个人思路 

        考虑需要反转字符串里的单词,且单词间仅保留一个空格,从尾部开始找到每个单词,将其正序加入ans字符串,并加上空格,循环结束后移除最后一个空格。

class Solution {
public:
    string reverseWords(string s) {
        string ans;
        int length = s.size();
        for(int i = length-1;i>=0;i--){
            string temp = "";//创建临时string temp用于存储逆序的逆序的单词
            if(s[i] == ' '){//当逆序此时结果为空时,continue
                continue;
            }
            while(i>= 0 && s[i]!=' '){//讲逆序的逆序的单词存入temp
                temp += s[i];
                i--;
            }
            reverse(temp.begin(),temp.end());//将temp中的单词翻转为正序
            ans += temp;//ans字符串拼接temp单词
            ans +=' ';//拼接完加空格
        }
        ans.erase(ans.end()-1);//移除最后一位空格
        return ans;
    }
};

算法的空间复杂度为O(n),时间复杂度为O(n)

双指针法

1.利用双指针法去多余空格

2.对s进行反转

3.对s内的所有单词进行反转

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 removeExtraSpaces(string& s) {//去除所有空格并在相邻单词之间添加空格, 快慢指针。
        int slow = 0;   //整体思想参考https://programmercarl.com/0027.移除元素.html
        for (int i = 0; i < s.size(); ++i) { //
            if (s[i] != ' ') { //遇到非空格就处理,即删除所有空格。
                if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
                while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
                    s[slow++] = s[i++];
                }
            }
        }
        s.resize(slow); //slow的大小即为去除多余空格后的大小。
    }

    string reverseWords(string s) {
        removeExtraSpaces(s); //去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
        reverse(s, 0, s.size() - 1);
        int start = 0; //removeExtraSpaces后保证第一个单词的开始下标一定是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;
    }
};

具体参考代码随想录,时间复杂度O(n),空间复杂度O(1)。

右旋转字符串

 如果不考虑空间的使用,创建新的string,根据位置再排序。

#include<iostream>
#include<string>
using namespace std;
int main(){
    int k;
    cin>>k;
    string s;
    cin>>s;
    string ans;
    int len = s.size();
    for(int i = len-k; i<len;i++){
        ans += s[i];
    }//len - k后的元素先加入ans
    for(int j = 0; j <len - k;j++){
        ans += s[j];
    }//前面元素后添加入ans
    cout<<ans<<endl;
}

空间复杂度O(1),时间复杂度O(n)。

考虑不申请额外空间,参考上题的双指针法的思路,对字符串先反转,后再对子串进行局部反转。

代码随想录 (programmercarl.com)

#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
int main(){
    int k;
    cin>>k;
    string s;
    cin>>s;
    string ans;
    int len = s.size();
    reverse(s.begin(),s.end());
    reverse(s.begin(),s.begin()+k);
    reverse(s.begin()+k,s.end());
    cout<<s<<endl;
}

空间复杂度O(1),时间复杂度O(n)。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值