代码随想录算法训练营 Day07 | 字符串 part01

344. 反转字符串

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

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

cpp 复制代码
class Solution {
public:
    // 解法:对撞双指针
    void reverseString(vector<char>& s) {
        // 左指针指向头部,右指针指向尾部
        int l = 0, r = s.size() - 1;
        // 指针相遇则停止
        while(l < r){
            // 交换左右指针的字符,然后左指针右移、右指针左移
            swap(s[l++], s[r--]);
        }
    }
};

总结

1. 对撞双指针

从字符串两端向中间遍历,交换对应元素,是字符串 / 数组反转的标准写法。

2. 复杂度

时间复杂度:O(n)(仅遍历一半字符)

空间复杂度:O(1)(原地操作,无额外空间)

3 .关键函数

swap():C++ 内置交换函数,简洁高效。


541. 反转字符串 II

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

  • 如果剩余字符少于 k 个,则将剩余字符全部反转。
  • 如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
cpp 复制代码
class Solution {
public:
    // 剩余 < k 个:全部反转;剩余 k~2k:反转前k个
    string reverseStr(string s, int k) {
        // 步长为 2k,按组遍历字符串
        for(int i=0;i<s.size();i+=2*k){
            // 反转从 i 开始的 k 个字符
            // min 处理边界:剩余字符不足 k 个时,反转到字符串末尾
            reverse(s.begin()+i, min(s.begin()+i+k, s.end()));
        }
        return s;
    }
};

总结

1. 解题思路

2k 为一个步长遍历字符串,将字符串分成若干组;

每组只反转前 k 个字符;

剩余字符数 < k:全部反转

剩余字符数 k ≤ 数量 < 2k:只反转前 k 个

min(起始位置+k, 末尾) 一行代码完美处理所有边界

2. 关键知识点
  1. 库函数 reverse:左闭右开区间 [begin, end),直接反转指定区间,底层就是双指针实现;
  2. 时间复杂度:O(n) 每个字符仅被反转一次,线性遍历;
  3. 空间复杂度:O(1) 原地操作,无额外空间开销。

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

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

cpp 复制代码
// 第一种写法:replace 库函数版
#include <iostream>
using namespace std;

int main(){
	string s;cin>>s;
	string mp="number";
	for(int i=0;i<s.size();i++){
		// 核心问题:替换后字符串变长,i会跳过字符!
		if(isdigit(s[i])) s.replace(i,1,mp);
	}
	cout<<s;
	return 0;
}

// 第二种写法:双指针从后往前原地修改
#include <iostream>
using namespace std;

int main(){
	string s;cin>>s;
	// 统计字符串中数字的个数
	int cnt=0;
    // 原字符串的最后一个字符下标
	int l=s.size()-1;
	for(int i=0;i<s.size();i++){
		if(isdigit(s[i])) cnt++;
	}
	// 扩容字符串:每个数字替换为number,长度增加5
	s.resize(s.size() + cnt*5);
	// 新字符串的最后一个字符下标
	int r=s.size()-1;
	// 核心:从后往前遍历(避免从前往后覆盖字符)
	while(l>=0){
		if(isdigit(s[l])){
			// 是数字:倒序填入 number
			s[r--]='r';
			s[r--]='e';
			s[r--]='b';
			s[r--]='m';
			s[r--]='u';
			s[r--]='n';
		}
		else{
			// 不是数字:直接复制到新位置
			s[r--]=s[l];
		}
		l--;
	}
	cout<<s;
	return 0;
}

总结

方法一
  • 致命 BUG:替换后字符串长度增加,字符后移,但循环变量 i 继续自增,会跳过下一个字符
  • 优点:代码极简,适合快速实现
  • 缺点:效率低,无法处理连续数字
方法二
  • 统计数字数量:计算替换后字符串的总长度
  • 预先扩容:避免频繁扩容导致效率降低
  • 双指针从后往前
    • 从前往后替换会覆盖未处理的字符
    • 从后往前替换,无覆盖、效率最高、原地修改
  • 倒序填充 number,完美匹配替换规则
相关推荐
luckycoding2 小时前
488. 祖玛游戏
算法·游戏·深度优先
8Qi82 小时前
LeetCode61. 旋转链表
c语言·数据结构·c++·算法·leetcode·链表·力扣
眼眸流转2 小时前
LeetCode热题100(一)
算法·leetcode
渡过晚枫2 小时前
[第十六届蓝桥杯/java/算法]1.偏蓝
java·算法·蓝桥杯
2501_940315262 小时前
【无标题】1302 层数最深叶子节点的和
java·数据结构·算法
isxhyeah2 小时前
python 数据结构 排序算法
数据结构·python·排序算法
invincible_Tang2 小时前
AcWing 796. 子矩阵的和 _
数据结构·算法
米粒12 小时前
力扣算法刷题 Day 8
算法·leetcode·职场和发展
Sakinol#2 小时前
Leetcode Hot 100 —— 普通数组
算法·leetcode