一、344. 反转字符串
双指针互换即可
class Solution {
public:
void reverseString(vector<char>& s) {
int i,j;
int size=s.size();
for(i=0,j=size-1;i<size/2;i++,j--){
swap(s[i],s[j]);
}
}
};
同时熟悉一下两个reverse函数
#include<cstring>
strrev(s)
#include<algorithm>
reverse(s.begin(),s.end());
二、541. 反转字符串 II
自己写的
为了处理逻辑:每隔2k个字符的前k的字符,写了一堆逻辑代码或者再搞一个计数器,来统计2k,再统计前k个字符。这算是常规思路,但是是麻烦了的。只要让i每次加2*k即可
class Solution {
public:
string reverseStr(string s, int k) {
int size = s.size();
int count = size / (k*2);
for(int j=0;j<count;j++){//前面 多个2*k的操作
for(int L=0+j*2*k,R=k-1+j*2*k;L<(k*2)+j*2*k;L++){
while(L<R){
swap(s[L++],s[R--]);
}
}
}
//最后一个2*k单独操作
if(size % (k*2) < k ){
for(int L=0+count*2*k,R=size % (k*2)-1+count*2*k;L<size % (k*2)+count*2*k;L++){
while(L<R){
swap(s[L++],s[R--]);
}
}
}else{
for(int L=0+count*2*k,R=k-1+count*2*k;L<(k*2)+count*2*k;L++){
while(L<R){
swap(s[L++],s[R--]);
}
}
}
return s;
}
};
看了题解后
时间复杂度其实没变,但是代码整洁了,只需要一次遍历,上面其实也是一次遍历,但是分成了几部分,会显得很复杂。
class Solution {
public:
string reverseStr(string s, int k) {
int size = s.size();
for(int i=0;i<size;i+=2*k){
if(i + k <= size){//前面还够2k个字符或剩余字符小于2k但大于等于k,即加上k还未到末尾
reverse(s.begin()+i,s.begin()+i+k);
}else{//最后面可能会不够完整的k,即加上k大于了size
reverse(s.begin()+i,s.end());
}
}
return s;
}
};
三、剑指 Offer 05. 替换空格
这题也是双指针,先增加s的长度,在从后往前遍历,遇到空格就变%20.
class Solution {
public:
string replaceSpace(string s) {
int count = 0;
int size = s.size();
for(int i=0;i<s.size();i++){
if(s[i] == ' '){
count++;
}
}
s.resize(s.size() + count * 2);
for(int i=size-1,j=s.size()-1;i<j;){
if(s[i] == ' '){
s[j--] = '0';
s[j--] = '2';
s[j--] = '%';
i--;
}else{
s[j--] = s[i--];
}
}
return s;
}
};
四、151. 反转字符串中的单词
for+erase去除空格
erase加上for循环,时间复杂度为O(n^2),删除的同时还会整体前移。
class Solution {
public:
string reverseWords(string s) {
//移除多余空格
for(int i=1;i<s.size();i++){
if(s[i] == ' ' && s[i] == s[i-1]){
s.erase(s.begin()+i);
i--;//需要注意到erase删除后,指针指向的数是发生了移动的,所以要回退一格
}
}
//或者从后往前 删除时自动回退了
// for (int i = s.size() - 1; i > 0; i--) {
// if (s[i] == s[i - 1] && s[i] == ' ') {
// s.erase(s.begin() + i);
// }
// }
//处理前导空格
while(s[0] == ' '){
s.erase(s.begin());
}
//处理尾随空格
while(s[s.size()-1] == ' '){
s.erase(s.end()-1);
}
//整个反转
reverse(s.begin(),s.end());
//每个单词反转
for(int i=0;i<s.size();){
int j=i;
while(s[j] != ' ' && j < s.size()){
j++;
}
reverse(s.begin()+i,s.begin()+j);
i = j+1;
}
return s;
}
};
双指针去除空格
因为不需要erase,而是遍历的过程中直接赋值,因此时间复杂度O(n),需要好好研究双指针。
class Solution {
public:
string reverseWords(string s) {
//移除多余空格
int slow=0,fast=0;
//前导空格
while(s[fast] == ' ' && fast < s.size()){
fast++;
}
while(fast < s.size()){
if(s[fast] != ' '){
s[slow++] = s[fast++];
}else{
//去除中间多余空格
while(s[fast] == ' ' && fast < s.size()){
fast++;
}
//补上单词间空格
s[slow++] = ' ';
}
}
//去除末尾可能多出来的一个空格
if(s[slow-1] == ' '){
s.resize(slow-1);
}else{
s.resize(slow);
}
//整个反转
reverse(s.begin(),s.end());
//每个单词反转
int start = 0;
for(int i=0;i<=s.size();i++){
if(s[i] == ' ' || i == s.size()){
reverse(s.begin()+start,s.begin()+i);
start = i+1;
}
}
return s;
}
};
五、剑指 Offer 58 - II. 左旋转字符串
暴力解
用一个额外数组可以很简单ac
class Solution {
public:
string reverseLeftWords(string s, int n) {
//暴力的话需要用一个辅助数组
string result;
result.resize(s.size());
int j = n;
int i=0;
while(j<s.size()){
result[i++] = s[j++];
}
j=0;
while(i<s.size()){
result[i++] = s[j++];
}
return result;
}
};
reverse
左旋数组在数据结构里做过几次,三次reverse就能原地左旋,时间复杂度O(n)
class Solution {
public:
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;
}
};
substr
注意一下字符串的复制可以用substr(pos, len)。不加参数会复制整个字符串。
六、总结
对于字符串,有很多内置函数还是不太熟悉,复制、求长度、反转、复制子串等,多用多熟。
对于Cpp的string,a+=b的效率比a=a+b的效率要高,因为a+=b可以直接在a的末尾追加b,而不需要创建一个新的string对象来存储a和b的和。而a=a+b则需要创建一个临时的string对象来保存a和b的和,然后再赋值给a。这样就会增加内存分配和拷贝的开销。
(4小时)