算法训练营Day08-LeetCode344. 反转字符串 && 541. 反转字符串 II && 151. 反转字符串中的单词 && KamaCoder54. 替换数字

344. 反转字符串

题目链接:LeetCode344. 反转字符串

编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。

不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题

思路:字符串首尾字符交换即可完成反转。定义首尾双指针同样可以。

code:

class Solution {
public:
    void reverseString(vector<char>& s) {
        int n = s.size();
        for(int i = 0; i < n / 2; i++){
            // 交换数值法
            // char temp = s[i];
            // s[i] = s[n - i - 1];
            // s[n - i - 1] = temp;

            // 通过位运算交换
            s[i] ^= s[n - i - 1];
            s[n - i - 1] ^= s[i];
            s[i] ^= s[n - i - 1];
        }
    }
};

总结:学习到了位运算交换法。


541. 反转字符串 II

题目链接:LeetCode541. 反转字符串 II

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

思路:遍历下标步长设置为 2k ,除非剩余字符少于 k 个,进行直接反转,否则反转前 k 个字符。

code:

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){
            if(i + k <= s.size()){
                reverse(s, i, i + k -1);
                continue;
            }
            reverse(s, i, s.size() - 1);
        }
        return s;
    }
};

总结:当需要以固定规律一段一段去处理字符串的时候,可以调整遍历下标步长,满足要求。


KamaCoder54. 替换数字

题目链接:KamaCoder54. 替换数字

给定一个字符串 s ,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number 。 例如,对于输入字符串 "a1b2c3",函数应该将其转换为 "anumberbnumbercnumber"

思考过程:暴力法,遍历字符串,统计数字字符出现次数;开辟新数组,数组长度为 小写字母个数 + 数字字符个数 * 6;然后填充新数组。但是占用空间较大。

调整思路:遍历字符串,统计数字字符出现次数;扩充原有数组,扩充后数组长度为 s.size() + 数字字符个数 * 5;使用双指针法,从后向前填充。

code:

#include <iostream>

using namespace std;

int main(){
    int cnt = 0;
    string s;
    cin >> s;
    int sOldIndex = s.size() - 1;
    for(int i = 0; i < s.size(); i++){
        if(s[i] <= '9' && s[i] >= '0'){
            cnt++;
        }
    }
    
    s.resize(sOldIndex + 1 + cnt * 5);
    int sNewIndex = s.size() - 1;
    
    while(sOldIndex >= 0){
        if(s[sOldIndex] <= '9' && s[sOldIndex] >= '0'){
            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;
}

总结:为什么要从后向前填充,从前向后填充不行么?

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

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

这么做有两个好处:

  1. 不用申请新数组。
  2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。

151. 反转字符串中的单词

题目链接:LeetCode151. 反转字符串中的单词

给你一个字符串 s ,请你反转字符串中 单词 的顺序。

单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。

返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。

注意:输入字符串 s 中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。

分析:题目要求将单词顺序反转,单词字母构成顺序不变。因此可以先将字符串整体反转,然后遇到单词再次反转即可达到要求。重点在于如何去除额外空格。这里先将空格元素全部移除,然后在相邻单词中间添加一个空格。

code:

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();){
            if(s[fast] != ' '){
                if(slow > 0){
                    s[slow++] = ' ';
                }
                while(s[fast] != ' ' && fast < s.size()){
                    s[slow++] = s[fast++];
                }
            }else{
                fast++;
            }
        }
        s.resize(slow);
    }

    string reverseWords(string s) {
        removeExtraSpace(s);
        reverse(s, 0, s.size() - 1);
        for(int i = 0, j = 0; i < s.size(); i++){
            while(s[i] != ' ' && i < s.size()) i++; // 统计每个单词字母个数
            reverse(s, j, i - 1);
            j = i + 1; // 记录下一个单词反转起始下标
        }
        return s;
    }
};

KamaCoder54. 替换数字

题目链接:KamaCoder54. 替换数字

字符串的右旋转操作是把字符串尾部的若干个字符转移到字符串的前面。给定一个字符串 s 和一个正整数 k,请编写一个函数,将字符串中的后面 k 个字符移到字符串的前面,实现字符串的右旋转操作。

例如,对于输入字符串 “abcdefg” 和整数 2,函数应该将其转换为 “fgabcde”。

思路:题目和 LeetCode151. 反转字符串中的单词 类似,甚至更简单。这里可以将由输入 k 得到的两部分子串,看成两个单词,依然是单词内部顺序不变,单词顺序反转。可通过先整体再局部反转实现。

code:

#include<iostream>
#include<algorithm>

using namespace std;

int main() {
    int k;
    string s;
    cin >> k;
    cin >> s;
    int length = s.size();

    reverse(s.begin(), s.end());
    reverse(s.begin(), s.begin() + k);
    reverse(s.begin() + k, s.end());
    
    cout << s << endl;
} 

总结:同样可以先局部反转再整体反转,注意下标变化。


参考文章:

代码随想录-字符串-541. 反转字符串> II
代码随想录-字符串-KamaCoder54. 替换数字
代码随想录-字符串-151. 反转字符串中的单词

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值