代码随想录DAY08 - 字符串 - 08/07

理论回顾

C++中的reverse函数

reverse(start,end) 参数是两个迭代器,分别是要反转的起始位置和终止位置,用于反转[start, end)范围内的元素顺序。

 #include <algorithm> // reverse 函数在此头文件中

C++中的resize函数

C++ 中的 resize 函数是用于调整容器大小的成员函数,常用于 vector、string 等标准库容器。

 resize(n); // 将容器大小改变为 n,新增的元素默认初始化
 resize(n,val); // 将容器大小改变为 n,新增的元素初始化为指定值 val

swap函数的两种实现

数值交换
int tmp = a;
a = b;
b = tmp;
位运算:使用异或操作符(‌^)‌
// 异或操作中,相同位为 0,不同位为 1
a ^= b; // 第一步:生成掩码并保存到a中
b ^= a; // 第二步:将a中保存的掩码与b进行异或,更新b的值
a ^= b; // 第三步:再次异或,完成a的更新并确保b的值是原始a的值

反转字符串

题干

题目:编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 char[] 的形式给出。不要给另外的数组分配额外的空间,你必须原地修改输入数组、使用 O(1) 的额外空间解决这一问题。你可以假设数组中的所有字符都是 ASCII 码表中的可打印字符。

链接:. - 力扣(LeetCode)

思考

双指针法:逆序输出其实就是头尾元素两两交换,用 left指针从头遍历,用 right 指针从尾遍历,两指针向中间靠拢,依次交换头尾元素。时间复杂度 O(n),空间复杂度 O(1)。

代码

双指针法
class Solution {
public:
    void swap(char &x,char &y){
        char tmp = x;
        x = y;
        y = tmp;
    }
    void reverseString(vector<char>& s) {
        // 如何原地修改,双指针法,两两交换
        int left = 0;
        int right = s.size()-1;
        while (left < right){
            swap(s[left],s[right]);
            left++;
            right--;
        }
    }
};

反转字符串 Ⅱ

题干

题目:给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 个字符中的前 k 个字符。如果剩余字符少于 k 个,则将剩余字符全部反转。如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。字符串 s 仅由小写英文字母组成。

链接:. - 力扣(LeetCode)

思考

双指针法:用下标 i 找到每 2k 个字符的起始位置(即 i 从 0 开始每次移动 2k 个单位),用 left 和 right 选定每 2k 个字符中的前 k 个字符,再对 left ~ right 范围内的字符串进行反转。对指定范围的元素进行反转,可以自己构造反转字符串的函数,参数为数组和 left、right 两个指针;也可以直接用 C++ 中的 reverse 函数。

代码

方法一:自己写字符串反转函数
class Solution {
public:
    void reversePartStr(string &s, int left, int right){
        while (left < right){
            swap(s[left],s[right]);
            left++;
            right--;
        }
    }
    string reverseStr(string s, int k) {
        int left = 0;
        int right = left+k-1;
        int i; // i 记录每 2k 个字符的起始位置,从 0 开始
        for (i = 0; i <= s.size(); i += 2*k){
            // 要判断 i + k 是否超过数组最大下标,超过则说明剩余的字符比 k 少,全部反转
            // 若没超过,则说明 剩余字符反转前 k 个即可
            if(i+k >= s.size()){
                // 剩余的字符数 比 k 少,全部反转
                right = s.size()-1;
                reversePartStr(s,left,right);
                break;
            } else{
                reversePartStr(s,left,right);
                left = i+2*k;
                right = left+k-1;
            }
        }
        return s;
    }
};
方法二:用C++的reverse函数
class Solution {
public:
    string reverseStr(string s, int k) {
        int i; // i 记录每 2k 个字符的起始位置,从 0 开始
        for (i = 0; i <= s.size(); i += 2*k){
            // 要判断 i + k 是否超过数组长度,超过 则说明剩余的字符比 k 少,全部反转
            // 若没超过,则说明 剩余字符反转前 k 个即可
            if(i+k > s.size()){
                reverse(s.begin()+i,s.end());
                break;
            } else{
                // 反转 前 k 个字符
                reverse(s.begin()+i,s.begin()+i+k);
            }
        }
        return s;
    }
};

替换数字

题干

题目:给定一个字符串 s,它包含小写字母和数字字符,请编写一个函数,将字符串中的字母字符保持不变,而将每个数字字符替换为number。

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

思路

方法一:用一个新的空字符串存储结果。遍历原来的字符串,当遍历到字母时,插入新字符串中;当遍历到数字,将“number”插入新字符串。

方法二双指针法:不需要额外的辅助空间,可以原地修改字符串。首先遍历一次数组,统计数字字符的个数 count,将原来的字符串长度扩大为每个数字替换成"number"的大小,即 数组长度 += count*5。第二次遍历数组时开始插入number,用指针 oldPtr 从旧数组的末尾往前遍历,用指针 newPtr 从数组扩充后的末尾往前遍历,每当 old 遍历到字母时,直接插入 new 的位置;每当 old 遍历到数字时,从 new 指针位置开始从后往前插入“number”,插入完更新 old 指针。

Q:为什么是从后往前遍历?而不是从前往后?

因为如果是从前往后遍历,在字符串中每次插入新字符,都需要将之后的元素依次往后移动,时间复杂度会是 O(n^2)。如果是从后往前遍历,可以在插入的同时移动元素,时间复杂度是 O(n)。

代码

方法一:使用辅助空间
#include <iostream>
#include <string>
using namespace std;

int main(){
    string str;
    getline(cin,str);
    string newStr;
    for (char c : str) {
        if (c <= '9' && c >= '0'){
            newStr += "number";
        } else{
            newStr.push_back(c);
        }
    }
    printf("%s",newStr.c_str());
    return 0;
}
方法二:双指针法
#include <iostream>
#include <string>
using namespace std;

int main(){
    string str;
    getline(cin,str);
    int countNum = 0; // 统计数字字符的个数
    int oldSize = str.size(); // 记录原字符串的长度
    for (char c : str) {
        if (c <= '9' && c >= '0'){
            countNum++;
        }
    }
    // 扩充字符串大小
    str.resize(str.size()+countNum*5);
    int oldPtr = oldSize-1;
    int newPtr = str.size()-1;
    while (oldPtr < newPtr){
        if (str[oldPtr] >= 'a' && str[oldPtr] <= 'z'){
            // old 遍历到字母,直接移动到末尾 new 位置
            str[newPtr--] = str[oldPtr];
        } else{
            // old 遍历到数字,在末尾 new 插入“number”
            str[newPtr--] = 'r';
            str[newPtr--] = 'e';
            str[newPtr--] = 'b';
            str[newPtr--] = 'm';
            str[newPtr--] = 'u';
            str[newPtr--] = 'n';
        }
        oldPtr--;
    }
    printf("%s",str.c_str());
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值