代码随想录算法训练营第八天 | 344.反转字符串,541.反转字符串II,剑指Offer05.替换空格,151.反转字符串中的单词,剑指Offer58-II.旋转字符串
一、参考资料
反转字符串
题目链接/文章讲解/视频讲解:https://programmercarl.com/0344.%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2.html
反转字符串II
题目链接/文章讲解/视频讲解:https://programmercarl.com/0541.%E5%8F%8D%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2II.html
剑指Offer05.替换空格
题目链接/文章讲解:https://programmercarl.com/%E5%89%91%E6%8C%87Offer05.%E6%9B%BF%E6%8D%A2%E7%A9%BA%E6%A0%BC.html
翻转字符串里的单词
题目链接/文章讲解/视频讲解:https://programmercarl.com/0151.%E7%BF%BB%E8%BD%AC%E5%AD%97%E7%AC%A6%E4%B8%B2%E9%87%8C%E7%9A%84%E5%8D%95%E8%AF%8D.html
剑指Offer58-II.左旋转字符串
二、LeetCode344.反转字符串
https://leetcode.cn/problems/reverse-string/description/
编写一个函数,其作用是将输入的字符串反转过来。输入字符串以字符数组 s 的形式给出。
不要给另外的数组分配额外的空间,你必须 原地 修改输入数组、使用 O(1) 的额外空间解决这一问题。
示例 1:
输入:s = ["h","e","l","l","o"] 输出:["o","l","l","e","h"]
示例 2:
输入:s = ["H","a","n","n","a","h"] 输出:["h","a","n","n","a","H"]
提示:
1 <= s.length <= 105
s[i] 都是 ASCII 码表中的可打印字符
class Solution {
public:
void reverseString(vector<char>& s) {
for(int i = 0, j = s.size() - 1; i < j; i++, j--) {
char tmp;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
// swap(s[i], s[j]);
}
}
};
这个题不难,仍有一个细节需要注意:
尽管i和j分别是字符串的两端,遍历结束的条件只能写做i < j,而不能是i != j。
反例:i = 2, j = 3;循环后变为i = 3, j = 2。
三、LeetCode541.反转字符串II
https://leetcode.cn/problems/reverse-string-ii/description/
给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。
如果剩余字符少于 k 个,则将剩余字符全部反转。
如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。
示例 1:
输入:s = "abcdefg", k = 2 输出:"bacdfeg"
示例 2:
输入:s = "abcd", k = 2 输出:"bacd"
提示:
1 <= s.length <= 104
s 仅由小写英文组成
1 <= k <= 104
注释版——非调用库函数
class Solution {
public:
// 非调用库函数版本
// 实现反转函数
void reverse (string &s, int start, int end) {
for (int i = start, j = end; i < j; i++, j--) {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
string reverseStr(string s, int k) {
// 找反转区间
for (int i = 0; i < s.size(); i += (2 * k)) {
// 情况一:每隔2k个字符的前k个字符反转
// 情况二:剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if (i + k <= s.size()) {
reverse(s, i, i + k - 1);
continue;
}
// 最后一步,剩余字符少于 k 个,则将剩余字符全部反转
reverse(s, i, s.size() - 1);
}
return s;
}
};
细节Tip:
1)for循环内记得写continue
2)等号可有可无,即i + k <= s.size() 或 i + k < s.size()
调用库函数版本
class Solution {
public:
// 调用库函数版本
// 花式反转
string reverseStr(string s, int k) {
for (int i = 0; i < s.size(); i += (2 * k)) {
if (i + k < s.size()) {
reverse(s.begin() + i, s.begin() + i + k);
continue;
}
reverse(s.begin() + i, s.end());
}
return s;
}
};
四、剑指Offer05.替换空格
https://leetcode.cn/problems/ti-huan-kong-ge-lcof/description/
请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1:
输入:s = "We are happy." 输出:"We%20are%20happy."
限制:
0 <= s 的长度 <= 10000
注释版
class Solution {
public:
string replaceSpace(string s) {
// 统计空格的个数
int count = 0;
for (int i = 0; i < s.size(); i++) {
if(s[i] == ' ') {
count++;
}
}
// 扩充字符串的长度
int sOldsize = s.size();
s.resize(s.size() + count * 2);
int sNewsize = s.size();
int j = sNewsize - 1;
int i = sOldsize - 1;
// 双指针操作,从后向前移动,将空格替换为%20
while(i < j) {
if (s[i] != ' ') {
s[j] = s[i];
} else {
s[j] = '0';
s[j - 1] = '2';
s[j - 2] = '%';
j -= 2;
}
i--;
j--;
}
return s;
}
};
再写一遍,加深理解
class Solution {
public:
string replaceSpace(string s) {
// 统计字符串中空格的个数
int count = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] == ' ') {
count++;
}
}
// 扩充字符串长度,记录先后的字符串大小
int sOld = s.size();
s.resize(s.size() + count * 2);
int sNew = s.size();
// 进行空格替换为%20,从后向前替换
for (int i = sOld, j = sNew; i < j; i--, j--) {
if (s[i] != ' ') {
s[j] = s[i];
} else {
s[j] = '0';
s[j - 1] = '2';
s[j - 2] = '%';
j -= 2;
}
}
return s;
}
};
五、LeetCode151.反转字符串中的单词
https://leetcode.cn/problems/reverse-words-in-a-string/description/
给你一个字符串 s ,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s 中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s中可能会存在前导空格、尾随空格或者单词间的多个空格。返回的结果字符串中,单词间应当仅用单个空格分隔,且不包含任何额外的空格。
示例 1:
输入:s = "the sky is blue"
输出:"blue is sky the"
示例 2:
输入:s = " hello world " 输出:"world hello" 解释:反转后的字符串中不能存在前导空格和尾随空格。
示例 3:
输入:s = "a good example" 输出:"example good a" 解释:如果两个单词间有多余的空格,反转后的字符串需要将单词间的空格减少到仅有一个。
提示:
1 <= s.length <= 104
s 包含英文大小写字母、数字和空格 ' '
s 中 至少存在一个 单词
进阶:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。
不使用库函数
class Solution {
public:
/*
解题思路:
去掉多余空格
整个字符串反转
每个单词反转
*/
// step1:移除多余的空格
// 去除所有空格并在相邻单词之间添加空格, 快慢指针
void removeExtraSpaces(string &s) {
int slow = 0;
for (int i = 0; i < s.size(); i++) {
// 遇到非空格就处理,即删除所有空格
if (s[i] != ' ') {
if (slow != 0) s[slow++] = ' '; //手动控制空格,给单词之间添加空格。slow != 0说明不是第一个单词,需要在单词前添加空格。
while (i < s.size() && s[i] != ' ') { //补上该单词,遇到空格说明单词结束。
s[slow++] = s[i++];
}
}
}
s.resize(slow); //slow的大小即为去除多余空格后的大小。
}
// step2:反转字符串s中左闭右闭的区间[start, end]
void reverse(string &s, int begin, int end) {
for (int i = begin, j = end; i < j; i++, j--) {
char tmp;
tmp = s[i];
s[i] = s[j];
s[j] = tmp;
// 或者swap(s[i], [j])
}
}
string reverseWords(string s) {
// 去除多余空格
removeExtraSpaces(s);
// 翻转所有字符串
reverse(s, 0, s.size() - 1);
// 去除多余空格后保证第一个单词的开始下标是0
int start = 0;
for (int i = 0; i <= s.size(); i++) {
//到达空格或者串尾,说明一个单词结束,进行翻转。
if (i == s.size() || s[i] ==' ') {
reverse(s, start, i - 1); //翻转,注意是左闭右闭 []的翻转
start = i + 1;
}
}
return s;
}
};
/*
总结:
三步走:一是移除所有多余的空格,二是整体字符串反转,三是单词内部反转
1. removeExtraSpaces函数中,if (slow != 0) s[slow++] = ' ';是防止在第一个单词之前添加空格,与while是并列的关系
2. s.resize(slow);重新设置将字符串的大小
3. 在进行单词内部反转的时候,注意边界条件i <= s.size(),反转时的区间范围为闭区间,从start到i-1
*/
使用库函数
class Solution {
public:
// 使用库函数版本
void removeExtraSpaces(string &s) {
int slow = 0;
for (int i = 0; i < s.size(); i++) {
if (s[i] != ' ') {
if (slow != 0) s[slow++] = ' '; // 单词之间添加空格
while (i < s.size() && s[i] != ' ') {
s[slow++] = s[i++];
}
}
}
s.resize(slow);
}
// void reverse(string &s, int begin, int end) {
// for (int i = begin, j = end; i < j; i++, j--) {
// char tmp = s[i];
// s[i] = s[j];
// s[j] = tmp;
// }
// }
string reverseWords(string s) {
removeExtraSpaces(s);
reverse(s.begin(), s.end());
// reverse(s.begin() + i, s.end());
// reverse(s, 0, s.size() - 1);
int start = 0;
for (int i = 0; i <= s.size(); i++) {
if (s[i] == ' ' || i == s.size()) {
// reverse(s, start, i - 1);
reverse(s.begin() + start, s.begin() + i);
start = i + 1;
}
}
return s;
}
};
六、剑指Offer58-II.旋转字符串
https://leetcode.cn/problems/zuo-xuan-zhuan-zi-fu-chuan-lcof/description/
字符串的左旋转操作是把字符串前面的若干个字符转移到字符串的尾部。请定义一个函数实现字符串左旋转操作的功能。比如,输入字符串"abcdefg"和数字2,该函数将返回左旋转两位得到的结果"cdefgab"。
示例 1:
输入: s = "abcdefg", k = 2 输出: "cdefgab"
示例 2:
输入: s = "lrloseumgh", k = 6 输出: "umghlrlose"
限制:
1 <= k < s.length <= 10000
如果有了前面几题的经验,这道旋转字符串将会非常简单
使用库函数——核心三行代码
class Solution {
public:
// 采用类似 反转字符串中的单词 一题的解法
// 思路:先反转前n个字符的子串、反转n+1到s.size()-1的子串,再整体反转
// 版本一:采用库函数 —— 边界注意一下
string reverseLeftWords(string s, int n) {
// 前一个子串反转
reverse(s.begin(), s.begin() + n);
// 后一个子串反转
reverse(s.begin() + n, s.end());
// 整体反转
reverse(s.begin(), s.end());
return s;
}
};
使用库函数——reverse函数
class Solution {
public:
// 实现reverse函数
void reverse(string &s, int begin, int end) {
for (int i = begin, j = end; i < j; i++, j--) {
char tmp = s[i];
s[i] = s[j];
s[j] = tmp;
}
}
string reverseLeftWords(string s, int n) {
reverse (s, 0, n - 1);
reverse (s, n, s.size() - 1);
reverse (s, 0, s.size() - 1);
return s;
}
};
细节Tip:对比库函数和非库函数的写法 —— 使用库函数的时候需要把reverse函数注释掉
总结:
字符串系列是近期做题最为顺利的刷题了;
C++的语法使用又熟练了一大步;
多思考、积累做题的思路,学到就多一分收获。
刷题加油鸭~~
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?