344-翻转字符串
这个题比较简单,主要考察实现一下reverse函数。
具体可以通过使用双指针left和right来实现,指向两端,同时交换数据然后移动。
时间复杂度为O(n),空间复杂度为O(1)。具体代码就不给出来啦,下面的541题中会用到翻转的操作,可以看一下下面541题的实现代码。
541-翻转字符串Ⅱ
题目比较简单,这里的rever函数实现就是双指针来实现字符串指定范围的翻转
class Solution {
public:
void rever(string &s, int left, int right){
while(left<right){
char temp;
temp=s[left];
s[left]=s[right];
s[right]=temp;
left++;
right--;
}
cout<<s<<endl;
}
string reverseStr(string s, int k) {
int num=0;
while(num<=s.size()){
if(num+k<=s.size()){
int left=num, right=num+k-1;
rever(s, left, right);
num+=2*k;
continue;
}
if(num+k>=s.size()){
int left=num, right=s.size()-1;
rever(s, left, right);
num+=2*k;
continue;
}
}
return s;
}
};
替换数字
比较简单,不多赘述,从前向后加就行,用一个额外的string来保存结果,然后采用双指针的思想,直接从前往后遍历就可以,不需要卡哥那样从后往前。
#include <iostream>
#include <string>
std::string replaceDigits(const std::string& s) {
std::string result;
for (char c : s) {
if (isdigit(c)) {
result += "number";
} else {
result += c;
}
}
return result;
}
int main() {
std::string s;
std::cout << "请输入字符串: ";
std::cin >> s;
std::cout << "转换后的字符串: " << replaceDigits(s) << std::endl;
return 0;
}
151.翻转字符串里的单词
题目描述
题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)
给你一个字符串 s
,请你反转字符串中 单词 的顺序。
单词 是由非空格字符组成的字符串。s
中使用至少一个空格将字符串中的 单词 分隔开。
返回 单词 顺序颠倒且 单词 之间用单个空格连接的结果字符串。
注意:输入字符串 s
中可能会存在前导空格、尾随空格或者单词间的多个空格。
返回的结果字符串中,需要去除所有的前导空格,需要去除所有的尾随空格,且单词间应当仅保留单个空格分隔。
示例:
我的代码思路:
- 去除尾部空格:使用 while 循环将 right 指针移动到第一个非空格字符。
- 寻找单词:内部 while 循环,将 left 指针移动到单词的开始位置。
- 提取单词:使用 s.substr(left + 1, right - left) 提取单词,并添加到 result 字符串。当然这个substr的逻辑我们也可以手动实现。
- 跳过单词之间的空格:在提取单词后,移动left指针,跳过单词之间的空格,直到left指向一个字符,然后让right=left,重复第二步寻找单词步骤。
- 去除尾部空格:在返回最终的字符串之前,检查字符串是否为空,如果不为空,删除字符串的最后一个字符(空格)。
注意:这里的第五步删除的空格并不是我们的串首空格,串首空格在第四步left指针已经忽略了,这里的空格是:
- 添加空格的逻辑: 在代码的 result += s.substr(left + 1, right - left) + " "; 这一行,每次提取一个单词后,都会在 result 字符串的末尾添加一个空格。
- 最后一个单词: 当逆序遍历到字符串的第一个单词时,也会执行上述添加空格的逻辑。因此,无论输入字符串的内容如何,最终的 result 字符串的末尾都会多出一个空格。
我的代码实现:
时间复杂度:O(n), 空间复杂度:O(n)
std::string reverseWords(std::string s) {
int n = s.size();
int right = n - 1;
std::string result = "";
// 去除尾部空格
while (right >= 0 && s[right] == ' ') {
right--;
}
int left = right;
while (right >= 0) {
// 寻找单词的开始
while (left >= 0 && s[left] != ' ') {
left--;
}
// 添加单词到结果字符串
result += s.substr(left + 1, right - left) + " ";
// 跳过单词之间的空格
while (left >= 0 && s[left] == ' ') {
left--;
}
right = left;
}
//去除尾部多余的空格
if (!result.empty()) {
result.pop_back();//移除多加的一个空格
}
return result;
}
原地操作代码思路:
这道题推可以尝试使用 O(1)
额外空间复杂度的 原地 解法,一开始我确实想不出来,让我们看看卡哥是如何做的。
想一下,我们将整个字符串都反转过来,那么单词的顺序指定是倒序了,只不过单词本身也倒序了,那么再把单词反转一下,单词不就正过来了。
所以解题思路如下:
- 移除多余空格
- 将整个字符串反转
- 将每个单词反转
举个例子,源字符串为:"the sky is blue "
- 移除多余空格 : "the sky is blue"
- 字符串反转:"eulb si yks eht"
- 单词反转:"blue is sky the"
这样我们就完成了翻转字符串里的单词。
代码实现:
void reverseWords(string &s) {
// 1. 去除多余空格
int slow = 0, fast = 0;
// 跳过前导空格
while (fast < str.size() && str[fast] == ' ') fast++;
//处理单词和间隔
while (fast < str.size()) {
if (str[fast] != ' ') {
str[slow++] = str[fast++];
} else {
// 先写入一个空格
if (slow > 0) str[slow++] = ' ';
// 跳过连续空格
while (fast < str.size() && str[fast] == ' ') fast++;
}
}
// 检查是否有误加的尾部空格,如果有的话就去掉尾部空格
if (str[slow - 1] == ' ')
slow--;
str.resize(slow); // 调整字符串大小
// 2. 反转整个字符串
reverse(s.begin(), s.end());
// 3. 反转每个单词
int start = 0, end = 0;
while (end < s.size()) {
if (s[end] == ' ') {
reverse(s.begin() + start, s.begin() + end);
start = end + 1;
}
end++;
}
reverse(s.begin() + start, s.end()); // 反转最后一个单词
}
双指针去除空格
在这个代码中,我觉得最需要我们来学习的点是这里的去除空格的操作,这里去除空格如果采用简单的remove操作的话,时间复杂度将会很大,因为对字符串的remove操作非常昂贵,去除这里的空格之后,后面整体的字符串都需要发生移动。一次remove的操作代价是O(N),多次操作的代价就是O(N^2),所以显然不能采用库函数remove。
这里去除空格的操作可以用到我们的老朋友:双指针!
思路大概就是快慢指针,
- 快指针 (
fast
) 用于遍历原始字符串,筛选出有效的字符。 - 慢指针 (
slow
) 用于记录新字符串的写入位置。
但是这里操作肯定没有这么简单啦!如果是将全部空格删去的话操作很简单,但这里对于两个相邻字符串之间的空格要保留一个,不能完全删除!!!
这里本质上还是双指针只不过逻辑判断处理要麻烦一点。
void remove_space(string& str){
int slow = 0, fast = 0;
// 1. 跳过前导空格
while (fast < str.size() && str[fast] == ' ') fast++;
// 2. 处理单词和间隔
while (fast < str.size()) {
if (str[fast] != ' ') {
str[slow++] = str[fast++];
} else {
// 先写入一个空格
if (slow > 0) str[slow++] = ' ';
// 跳过连续空格
while (fast < str.size() && str[fast] == ' ') fast++;
}
}
// 3. 检查是否有误加的尾部空格,如果有的话就去掉尾部空格
if (str[slow - 1] == ' ')
slow--;
str.resize(slow); // 调整字符串大小
// **测试输出**
cout << str << endl;
}
KMP算法
用于在一个文本字符串(text)中查找一个模式字符串(pattern)的出现。KMP 算法避免了不必要的回溯,从而提高了匹配效率。
时间复杂度为 O(m + n),其中 n 是文本字符串的长度,m 是模式字符串的长度。
KMP暂时看不懂,虽然能会算,但是理解不了原理。