代码随想录day8 | LeetCode344. 反转字符串、LeetCode541. 反转字符串 II、KamaCoder54. 替换数字(第八期模拟笔试)

代码随想录day8 | LeetCode344. 反转字符串、LeetCode541. 反转字符串 II、KamaCoder54. 替换数字(第八期模拟笔试)

反转字符串

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

自己敲

双指针法

class Solution {
public:
    void reverseString(vector<char>& s) {
        char *p,*q;
        p=&s[0],q=&s[0];
        q+=s.size()-1;
        while(p<q){
            swap(*p,*q);
            p++;
            q--;
        }
    }
};
class Solution {
public:
    void reverseString(vector<char>& s) {
        char *p=s.data(),*q=&s.back();
        while(p<q){//指针小于,不是指针所指的数值
            swap(*p,*q);
            p++;
            q--;
        }
    }
};

看题解

链接

(直接用C++里的一个库函数 reverse)

如果在现场面试中,我们什么时候使用库函数,什么时候不要用库函数呢?

如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数。

如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数。

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]);
        }
    }
};

总结

  • 有下标的时候就不用再创建指针了,多此一举,下标即指针(vector底层是连续空间)

  • swap可以有两种实现。

    • 一种就是常见的交换数值:
    int tmp = s[i];
    s[i] = s[j];
    s[j] = tmp;
    
    • 一种就是通过位运算:
    s[i] ^= s[j];
    s[j] ^= s[i];
    s[i] ^= s[j];
    

反转字符串Ⅱ

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

自己敲

class Solution {
public:
    string reverseStr(string s, int k) {
        int begin=0,end=2*k+begin-1;
        while(1){
            if(s.size()-begin<k){
                reverse(s,begin,s.size()-begin-1);
                break;
            }else if(s.size()-begin>=k&&s.size()-begin<2*k){
                reverse(s,begin,begin+k-1);
                break;
            }else{
                reverse(s,begin,begin+k-1);
            }
            begin+=2*k;
            end+=2*k;
        }
        return s;
    }

    void reverse(string &s,int begin,int end){
        while(begin<end){
            swap(s[begin],s[end]);
            begin++;
            end--;
        }
    }
};

部分测试用例未通过
在这里插入图片描述

看题解

链接

这种题目属于模拟

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

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

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

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

class Solution {
public:
    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.begin() + i, s.begin() + i + k );
            } else {
                // 3. 剩余字符少于 k 个,则将剩余字符全部反转。
                reverse(s.begin() + i, s.end());
            }
        }
        return s;
    }
};

如果要自己实现reverse,注意保持区间一致性(不要一会儿左闭右闭一会儿左闭右开),以下是左闭右闭代码

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

总结

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

没找到代码错哪了

觉得下标和个数之间关系要好好理解

替换数字

题目链接:KamaCoder54. 替换数字(第八期模拟笔试)

自己敲

用了辅助空间

#include<iostream>
#include<string>
#include<vector>
using namespace std;

int main(){
    string s;
    cin>>s;
    vector<char> result;
    for(int i=0;i<s.size();i++){
        if(s[i]>='a'&&s[i]<='z'){
            result.push_back(s[i]);
        }
        if(s[i]>='0'&&s[i]<='9'){
            result.push_back('n');
            result.push_back('u');
            result.push_back('m');
            result.push_back('b');
            result.push_back('e');
            result.push_back('r');
        }
    }
    for(int i=0;i<result.size();i++){
        cout<<result[i];
    }
    return 0;
}

看题解

链接

不使用额外辅助空间

首先扩充数组到每个数字字符替换成 “number” 之后的大小。

例如 字符串 “a5b” 的长度为3,那么 将 数字字符变成字符串 “number” 之后的字符串为 “anumberb” 长度为 8。

如图:
在这里插入图片描述

然后从后向前替换数字字符,也就是双指针法,过程如下:i指向新长度的末尾,j指向旧长度的末尾。

在这里插入图片描述

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

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

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

这么做有两个好处:

  1. 不用申请新数组。
  2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
#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;       
    }
}

总结

  • 数组填充类的问题:先预先给数组扩容带填充后的大小,然后在从后向前进行操作

  • C++中字符串和字符数组

    • 在C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用’\0’来判断是否结束。
    • 那么vector< char > 和 string 在基本操作上没有区别,但是 string提供更多的字符串处理的相关接口,例如string 重载了+,而vector却没有。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值