《代码随想录》刷题打卡day7:字符串part01

【344. 反转字符串】

题目链接
练习时不要使用reverse函数,了解一下反转字符串的原理。

如已清楚了解原理且库函数仅仅是解题步骤中的一小部分,可以使用库函数。

class Solution {
public:
    void reverseString(vector<char>& s) {
        int len = s.size();
        for(int i=0;i<len/2;i++){
            char tmp = s[i];
            s[i] = s[len-i-1];
            s[len-i-1] = tmp;
            /*
            下面是用位异或运算进行变量值交换的写法:
            s[i] ^= s[j];
            s[j] ^= s[i];
            s[i] ^= s[j];
            
            这三行代码是一种不借助临时变量交换两个变量值的经典写法,利用了异或(^)运算的特性:
            第一行 s[i] ^= s[j]:等价于 s[i] = s[i] ^ s[j],此时s[i]存储了原始值的异或结果
            第二行 s[j] ^= s[i]:等价于 s[j] = s[j] ^ s[i],借助异或的性质(a ^ (a ^ b) = b),此时s[j]已变为原始s[i]的值
            第三行 s[i] ^= s[j]:此时s[i]会变为原始s[j]的值
            最终实现了s[i]和s[j]两个变量值的交换,这种方法比使用临时变量更节省内存空间,但可读性稍差。
            */
        }
    }
};
【541. 反转字符串II】

题目链接
思路简单,判断好每次循环开始和结束的位置就好。

class Solution {
public:
    string reverseStr(string s, int k) {
        int l = s.size(); // l表示剩余字符长度
        int p = s.size()-l; // p表示每一次起始遍历位置
        // 1. 每隔 2k 个字符的前 k 个字符进行反转
        while(l>=2*k){
            reverse(s.begin()+p,s.begin()+p+k);
            l-=2*k;
            p+=2*k;
        }
        // 2. 剩余字符少于 k 个,则将剩余字符全部反转。
        if(l<k) reverse(s.begin()+p,s.end());
        // 3. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
        if(l<2*k&&l>=k) reverse(s.begin()+p,s.begin()+p+k);
        return s;
    }
};
【54. 替换数字】

题目链接
如果想把这道题目做到极致,就不要只用额外的辅助空间了!

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

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

# include<iostream>
# include<string>

using namespace std;

int main(){
    string s;
    cin>>s;
    int old_pos = s.size()-1;
    int count = 0;
    for(int i=0;i<s.size();i++){
        if(s[i]<='9'&&s[i]>='0'){
            count++;
        }
    }
    s.resize(s.size()+count*5);
    int new_pos = s.size()-1;
    while(old_pos>=0){
        if(s[old_pos]>='0'&&s[old_pos]<='9'){
            s[new_pos--] = 'r';
            s[new_pos--] = 'e';
            s[new_pos--] = 'b';
            s[new_pos--] = 'm';
            s[new_pos--] = 'u';
            s[new_pos--] = 'n';
            old_pos--;
        }else{
            s[new_pos--] = s[old_pos];
            old_pos--;
        }
    }
    cout<<s<<endl;
}

然后从后向前替换数字字符,也就是双指针法,过程如下:i指向新长度的末尾,j指向旧长度的末尾。
在这里插入图片描述
为什么要从后向前填充,从前向后填充不行么?

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

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

这么做有两个好处:

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

拓展:字符串和数组有什么差别

字符串是若干字符组成的有限序列,也可以理解为是一个字符数组,但是很多语言对字符串做了特殊的规定,接下来我来说一说C/C++中的字符串。

在C语言中,把一个字符串存入一个数组时,也把结束符 '\0’存入数组,并以此作为该字符串是否结束的标志。

例如这段代码:

char a[5] = "asd";
for (int i = 0; a[i] != '\0'; i++) {
}

在C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用’\0’来判断是否结束。

例如这段代码:

string a = "asd";
for (int i = 0; i < a.size(); i++) {
}

那么vector< char > 和 string 又有什么区别呢?

其实在基本操作上没有区别,但是 string提供更多的字符串处理的相关接口,例如string 重载了+,而vector却没有。

所以想处理字符串,我们还是会定义一个string类型。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值