一、反转字符串
1、力扣题目
题目链接:344. 反转字符串 - 力扣(LeetCode)(该题建议手动实现反转
2、初次解题
有两个思路
第一个是直接用c++中string自带的反转函数
代码如下:
class Solution {
public:
void reverseString(vector<char>& s) {
reverse(s.begin(),s.end());//反转函数
}
};
第二个是手动实现反转,用双指针,两两交换
代码如下:
class Solution {
public:
void reverseString(vector<char>& s) {
int left=0;
int right=s.size()-1;
while(left<right){
char a=s[left];
s[left]=s[right];
s[right]=a;
left++;
right--;
}
}
};
3、视频学习
视频链接:字符串基础操作! | LeetCode:344.反转字符串_哔哩哔哩_bilibili
简单看了一下,跟视频跟上述思路差不多,就不再赘述。不过他在交换时用到了swap函数,上述代码中swap也手动实现了。
二、反转字符串
1、力扣题目
题目链接:541. 反转字符串 II - 力扣(LeetCode)
2、初次解题
因为刚开始用c++,所以对一些函数的确切用法没有仔细了解,在c++的reverse中是前闭后开区间,导致想了半天,应多留意函数的使用规则。
s.begin()指向第一个元素,s.end()指向最后一个元素的后面。
对于一些分组求的情况,i 每次增加一组的量,这样可以保证分组进行处理。
不过刚开始我对剩余数目大于2k,大于k小于2k,小于k三种情况都分别进行了讨论,不过后面发现后两种情况可以合并。
class Solution {
public:
string reverseStr(string s, int k) {
for(int i=0;i<s.size();i+=2*k){//分组处理
int last=s.size()-i-1;//首先判断剩余字符数
if(last<k){//比k小,反转剩下全部
reverse(s.begin()+i,s.end());
break;
}
else{
reverse(s.begin()+i,s.begin()+i+k);//应该是i+k,因为是下标[i,i+k)包括i的k个
}
}
return s;
}
};
3、视频学习
视频链接:字符串操作进阶! | LeetCode:541. 反转字符串II_哔哩哔哩_bilibili
本质思路差不多,有需要的可以看一下
4、总结
刚开始想的时候有点偏了,遍历的时候i++,想着怎么确定2k个字符,后面才反应过来i+=2k就行了,下次应该注意这种分组处理的情况。
三、替换数字
1、卡码网题目
题目链接:54. 替换数字(第八期模拟笔试) (kamacoder.com)
2、初次解题
看完题目想的就是直接再定义一个string作为输出结果,处理的时候字符链接就行。
代码如下:
#include<iostream>
#include<string>
using namespace std;
int main(){
string a;
cin >> a;
string b;
for(int i=0;i<a.size();i++){
if(a[i]-'a'>=0&&'z'-a[i]>=0){b+=a[i];}
else{
b+="number";//注意是双引号
}
}
cout << b << endl;
}
3、文章学习
上述思路利用了另一个字符串,要是空间复杂度为O(1)的话,那么就需要首先遍历一遍字符串,统计一下数字出现的次数,然后s.resize(s.size()+count*5),此处需注意是*5,因为"number"中有一个字母占据数字的位置,所以另外需要的空间大小就是5。
然后进行倒序遍历,代码我就不写了,就思路理解一下吧。
如下图所示,图片来自代码随想录:
4、总结
注意单引号是字符,双引号是字符串。然后理解空间复杂度为O(1)的解法,注意倒序遍历。
四、反转字符串的字母
1、力扣题目
题目链接:151. 反转字符串中的单词 - 力扣(LeetCode)
2、初次解题
想法是对先整个字符串进行反转,然后再对单个单词进行反转,最后删除空格
单词反转思路:
1、在原字符串末尾增加一个空格
2、使用双指针,指向单词开头第一个字母,right指向单词后的第一个空格。reverse(left,right)之后left=right。
3、为了确保left指向第一个字母,当left指向空格时,left++后跳转下一次循环。
因为最后一个字符一定为空,所以最后一次left一定在left++,因而循环条件为left<s.size(),最后可以跳出循环且完成了遍历。
删除空格思路:
如果当前和前一个相等且都为空格,则从删除当前元素,之后判断首位是否为空,是则删除首位空格,最后删除末尾的空格。
代码如下:
class Solution {
public:
string reverseWords(string s) {
reverse(s.begin(),s.end());//反转字符串
s+=' ';//便于后续操作
//左右指针
int left=0;//单词开头第一个字母
int right=0;//单词最后一个字母
while(left<s.size()){
if(s[left]==' '){left++;continue;}
right=left;//第一个不为空格的
while(s[right]!=' ')right++;
reverse(s.begin()+left,s.begin()+right); //反转单词
left=right;
}
//使用迭代器遍历删除
string::iterator itr=next(s.begin());
while(itr!=s.end()){
if(*itr==' '&&*prev(itr)==' '){
s.erase(itr);
continue;
}
itr++;
}
if(*s.begin()==' ')s.erase(s.begin());
s.erase(s.end()-1);
return s;
}
};
3、视频学习
视频链接:字符串复杂操作拿捏了! | LeetCode:151.翻转字符串里的单词_哔哩哔哩_bilibili
大致思路跟上述一下一致,不过视频是先删除的空格,然后再处理单个单词的时候。此处主要是删除空格的方法的改进。
使用双指针,一个快,一个慢。快指针遍历字符串,仅当指向字母时进行处理,这里相当于删除了所有空格。
慢指针在单词前手动添加一个空格(首单词除外)即可。
这里仅给出双指针删除的代码:
//双指针删除,快慢指针
int slow = 0;
for (int fast = 0; fast < s.size(); fast++) {
//遇到非空格就处理,即删除所有空格
if (s[fast]!=' ') { //(fast代表所有单词,不含空格)
//给单词之间添加空格,除了第一个单词,其余都需要在单词前添加空格。
if (slow != 0){
s[slow]=' ';
slow++;
}
//单词复制,遇到空格说明单词结束。
while (fast < s.size() && s[fast] != ' ') {
s[slow]=s[fast];
fast++;
slow++;
}
}
}
s.resize(slow); //去除多余空格。
4、总结
反转的时候用双指针,到删除的时候就没想起来,应注意当在数组更新时,可以考虑使用快慢指针。
五、右旋字符串
1、卡码网习题
题目链接:55. 右旋字符串(第八期模拟笔试) (kamacoder.com)
2、初次解题
可能是因为上一题连着写,所以突然注意到,将整个字符串反转过来,然后再分段进行反转,就可以得到答案。
代码如下:
#include<iostream>
#include<string>
#include <algorithm>
using namespace std;
int main(){
int n;
string a;
cin >> n;
cin >> a;
reverse(a.begin(),a.end());//整体反转
reverse(a.begin(),a.begin()+n);//反转前n个
reverse(a.begin()+n,a.end());//反转剩余字符
scout << a;
}
3、文章学习
看了一下思路,跟上述一样。如果是左旋转的话,其实就是右旋( s.size() - n )。
六、总结
单解题的话难度其实不大,但是注意优化的思路,空间复杂度为O(1)的解题思路。