今天学习的内容是字符串相关内容。
1. 反转字符串
这是一个字符串基本操作,有库函数实现的方法。不使用库函数的方法即双指针
class Solution {
public:
void reverseString(vector<char>& s) {
for(int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--){
char a=s[i];
s[i]=s[j];
s[j]=a;
}
}
};
顺便学习一下使用库函数的方法,c++中使用头文件#include<algorithm>即可使用reverse函数
#include<algorithm>
class Solution {
public:
void reverseString(vector<char>& s) {
reverse(s.begin(),s.end());
}
};
2. 反转字符串2
字符串每遍历2k个字符反转前k个字符串,并且剩余不足2k的字符若大于k反转前k个字符串,小于k则全部反转。基本操作是相似的,但是增加了很多判断操作。
这道题关键的部分在于剪枝,去掉遍历中不是2k整数倍的元素,就可以大大的降低复杂度。如果不在这一步做剪枝处理,会导致后续的判例变得很复杂而且超时。
class Solution {
public:
string reverseStr(string s, int k) {
for(int i=0;i<s.size();i+=(2*k)){
if(i+k>s.size()){
for(int x=i,y=s.size()-1;x<y;x++,y--){
swap(s[x],s[y]);
}
}
else {
for(int x=i,y=i+k-1;x<y;x++,y--){
swap(s[x],s[y]);
}
}
}
return s;
}
};
也可以自己定义一个类似库函数reverse的方法。
class Solution {
public:
void reverse(string& s,int start,int end){
for(int i=start,j=end;i<j;i++,j--){
swap(s[i],s[j]);
}
}
string reverseStr(string s, int k) {
for(int i=0;i<s.size();i+=(2*k)){
if(i+k>s.size()){
reverse(s,i,s.size()-1);
}
else {
reverse(s,i,i+k-1);
}
}
return s;
}
};
题例 :541 反转字符串2
3. 替换空格
把字符串中每一个空格替换成“20%”。替换的20%是一个字符串,所以应该在空格下标insert()函数插入。但是这样复杂度不满足条件。
考虑到字符串的性质,创建一个新字符串result,遍历的过程中给result加上字符串是s[i],s[i]是 ' ' 空格的情况下,加上'%'+'2'+'0',返回result字符串即可。这种方法需要为新字符串开启存储空间。
class Solution {
public:
string replaceSpace(string s) {
string result;
for(int i=0;i<s.size();i++){
if(s[i]==' '){
result=result+'%'+'2'+'0';
}
else result=result+s[i];
}
return result;
}
};
使用双指针的方法,先计算需要给插入字符串让出的空间,再让两个指针同时从新旧字符串尾部位置向前遍历,不为空就赋值到新位置,遇到空值就向前赋值。这样的方式更加节省空间。
class Solution {
public:
string replaceSpace(string s) {
int count =0;
for(int i=0;i<s.size();i++){
if(s[i]==' '){
count++;
}
}
int old=s.size();
s.resize(s.size()+count*2); //加上原本的空格,每次插入需要2个额外的字符。
int renew=s.size();
for(int i=old-1, j=renew-1; j>i ;i--,j--){
if(s[i]!=' '){
s[j]=s[i];
}
else{
s[j]='0';
s[j-1]='2';
s[j-2]='%';
j-=2;
}
}
return s;
}
};
题例:替换空格
4. 翻转字符串的单词
这道题的难点在于单词长度不一致,用双指针直接翻转的话会导致很多错误。可以使用额外存储空间的话分割字符串再从尾到头生成新字符串。
不使用额外空间的方法,先把字符串全部翻转,再从头开始,把每一个单词翻转。需要注意!erase()函数本身复杂度为O(n),而不是O(1)。使用快慢指针除去多余的空格更方便。
这道题要分成三个部分来处理,首先是翻转字符串函数,按照前几题的写法即可
void reverse(string& s,int start,int end){
for(int i=start,j=end;i<j;i++,j--){
swap(s[i],s[j]);
}
}
第二部分是除去多出的空格,这一部分逻辑比较难理解,快指针顺序遍历,把快指针的值赋给慢指针位置,慢指针负责创建空格。
void exceptspace(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]!=' '){ //通过while循环把空格后的单词填充进去
s[slow++]=s[i++];
}
}
s.resize(slow);
}
翻转处理完的字符串逻辑就比较简单了。
string reverseWords(string s) {
reverse(s,0,s.size()-1);
exceptspace(s);
int count=0;
for(int i=0;i<=s.size();++i){
if(s[i]==' '||i==s.size()){
reverse(s,count,i-1);
count=i+1;
}
}
return s;
}
题例:反转字符串中的单词
5. 左旋转字符串
把左侧前n个字符放到右侧。逻辑上不难,但是不额外使用空间的方法比较巧妙。先反转前n个字符串,再全部反转,再反转前s.size()-n个字符串。解题时要多注重数学逻辑的思考。
class Solution {
public:
void reverse(string& s,int start,int end){
for(int i=start,j=end;i<j;i++,j--){
swap(s[i],s[j]);
}
}
void reverseLeft(string& s,int n){
reverse(s,0,n-1);
reverse(s,0,s.size()-1);
reverse(s,0,s.size()-n-1);
}
string reverseLeftWords(string s, int n) {
reverseLeft(s,n);
return s;
}
};
题例:左旋反转字符串
创建新字符串的方法,作练习:
class Solution {
public:
string reverseLeftWords(string s, int n) {
string result="";
for (int i = n; i < s.size(); i++) result += s[i];
for (int i = 0; i < n; i++) result += s[i];
return result;
}
};