【344. 反转字符串】
题目链接
练习时不要使用reverse函数,了解一下反转字符串的原理。
如已清楚了解原理且库函数仅仅是解题步骤中的一小部分,可以使用库函数。
class Solution {
public:
void reverseString(vector<char>& s) {
int len = s.size();
for(int i=0;i<len/2;i++){
char tmp = s[i];
s[i] = s[len-i-1];
s[len-i-1] = tmp;
/*
下面是用位异或运算进行变量值交换的写法:
s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];
这三行代码是一种不借助临时变量交换两个变量值的经典写法,利用了异或(^)运算的特性:
第一行 s[i] ^= s[j]:等价于 s[i] = s[i] ^ s[j],此时s[i]存储了原始值的异或结果
第二行 s[j] ^= s[i]:等价于 s[j] = s[j] ^ s[i],借助异或的性质(a ^ (a ^ b) = b),此时s[j]已变为原始s[i]的值
第三行 s[i] ^= s[j]:此时s[i]会变为原始s[j]的值
最终实现了s[i]和s[j]两个变量值的交换,这种方法比使用临时变量更节省内存空间,但可读性稍差。
*/
}
}
};
【541. 反转字符串II】
题目链接
思路简单,判断好每次循环开始和结束的位置就好。
class Solution {
public:
string reverseStr(string s, int k) {
int l = s.size(); // l表示剩余字符长度
int p = s.size()-l; // p表示每一次起始遍历位置
// 1. 每隔 2k 个字符的前 k 个字符进行反转
while(l>=2*k){
reverse(s.begin()+p,s.begin()+p+k);
l-=2*k;
p+=2*k;
}
// 2. 剩余字符少于 k 个,则将剩余字符全部反转。
if(l<k) reverse(s.begin()+p,s.end());
// 3. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if(l<2*k&&l>=k) reverse(s.begin()+p,s.begin()+p+k);
return s;
}
};
【54. 替换数字】
题目链接
如果想把这道题目做到极致,就不要只用额外的辅助空间了!
首先扩充数组到每个数字字符替换成 “number” 之后的大小。
例如 字符串 “a5b” 的长度为3,那么 将 数字字符变成字符串 “number” 之后的字符串为 “anumberb” 长度为 8。
# include<iostream>
# include<string>
using namespace std;
int main(){
string s;
cin>>s;
int old_pos = s.size()-1;
int count = 0;
for(int i=0;i<s.size();i++){
if(s[i]<='9'&&s[i]>='0'){
count++;
}
}
s.resize(s.size()+count*5);
int new_pos = s.size()-1;
while(old_pos>=0){
if(s[old_pos]>='0'&&s[old_pos]<='9'){
s[new_pos--] = 'r';
s[new_pos--] = 'e';
s[new_pos--] = 'b';
s[new_pos--] = 'm';
s[new_pos--] = 'u';
s[new_pos--] = 'n';
old_pos--;
}else{
s[new_pos--] = s[old_pos];
old_pos--;
}
}
cout<<s<<endl;
}
然后从后向前替换数字字符,也就是双指针法,过程如下:i指向新长度的末尾,j指向旧长度的末尾。
为什么要从后向前填充,从前向后填充不行么?
从前向后填充就是**O(n^2)**的算法了,因为每次添加元素都要将添加元素之后的所有元素整体向后移动。
其实很多数组填充类的问题,其做法都是先预先给数组扩容带填充后的大小,然后在从后向前进行操作。
这么做有两个好处:
1. 不用申请新数组。
2. 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
拓展:字符串和数组有什么差别
字符串是若干字符组成的有限序列,也可以理解为是一个字符数组,但是很多语言对字符串做了特殊的规定,接下来我来说一说C/C++中的字符串。
在C语言中,把一个字符串存入一个数组时,也把结束符 '\0’存入数组,并以此作为该字符串是否结束的标志。
例如这段代码:
char a[5] = "asd";
for (int i = 0; a[i] != '\0'; i++) {
}
在C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用’\0’来判断是否结束。
例如这段代码:
string a = "asd";
for (int i = 0; i < a.size(); i++) {
}
那么vector< char > 和 string 又有什么区别呢?
其实在基本操作上没有区别,但是 string提供更多的字符串处理的相关接口,例如string 重载了+,而vector却没有。
所以想处理字符串,我们还是会定义一个string类型。