第八天,✊o(* ̄▽ ̄*)ブ✊,编程语言:C++;
目录
344.反转字符串
文档讲解:代码随想录反转字符串
视频讲解:手撕反转字符串
题目:
初看:第一想法是采用一对左右指针,分别指向字符串初始位置和终点位置,之后两两进行交换。再不断往内缩进,循环判断left < right。
注意:本题直接采用库函数reverse( )同样可以实现字符串反转,但本题不适合直接使用库函数,一般来说,如果题目关键的部分直接用库函数就可以解决,建议不使用库函数。而如果库函数仅仅是解题过程中的一小部分,可以考虑使用库函数。
本题使用双指针法,和数组中的双指针法基本一样,因为字符串也是一种数组,元素再内存中是连续分布的。
代码:
//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
void reverseString(vector<char>& s) {
//设置左右指针
int left = 0;
int right = s.size() - 1;
while (left < right) {
char tmp = s[left];
s[left] = s[right];
s[right] = tmp;
left++;
right--;
}
}
};
541.反转字符串II
文档讲解:代码随想录反转字符串II
视频讲解:手撕反转字符串II
题目:
初看: 第一想法是首先构造一个交换函数,交换k个字符。其次通过一个循环,每次取出2k个字符的前k个字符。最后单独处理不足2k个字符的情况,如果剩下的超过k个字符,就取出k个字符,不超过k个字符,则取出到end()的字符。
//时间复杂度O(n)
//空间复杂度O(n)
class Solution {
public:
void swapstring(string &s, int left, int right) {
while (left < right) {
swap(s[left], s[right]);
left++;
right--;
}
}
string reverseStr(string s, int k) {
int n = s.size();
int loop = n / (2*k);
int left= 0;
int right = k - 1;
while(loop--) {
swapstring(s, left, right);
left += 2*k;
right += 2*k;
}
right = min(right, n - 1);
swapstring(s, left, right);
return s;
}
};
学习:本题不同于上一题,关键部分在于如何取出前k个字符,因此可以直接采取库函数reverse()反转k个字符。
代码:
//时间复杂度O(n)
//空间复杂度O(1)
class Solution {
public:
string reverseStr(string s, int k) {
for (int i = 0; i < s.size(); i += (2 * k)) {
// 1. 每隔 2k 个字符的前 k 个字符进行反转
// 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if (i + k <= s.size()) {
reverse(s.begin() + i, s.begin() + i + k );
} else {
// 3. 剩余字符少于 k 个,则将剩余字符全部反转。
reverse(s.begin() + i, s.end());
}
}
return s;
}
};
收获:
- 此处循环要注意i的变化,不能简单的写为i++,这样的话,还需要设置一个count值来计数2k个。for循环每个部分的含义要搞清楚,当需要固定规律一段一段去处理字符串说的时候,要想想在for循环的表达式上做文章。
卡码网54.替换数字
文档讲解:代码随想录替换数字
题目:
初看:第一想法是,首先创建一个新的字符串ans,再设置两个指针分别指向给定的字符串s和新字符串ans的初始位置。之后遍历字符串s,当遇到数字时,字符串ans+=“number”,当不是数字时,ans+=s[i]。
代码:
//时间复杂度O(n)
//空间复杂度O(n)
#include <iostream>
using namespace std;
int main() {
string s;
cin >> s;
string ans;
int i = 0;
int j = 0;
for ( ; i < s.size(); i++) {
if(s[i] >= '0' && s[i] <= '9') {
ans += "number";
}
else {
ans += s[i];
}
}
cout << ans << endl;
system("pause");
return 0;
}
学习: 本题也可以不创建一个新的字符串,而是先通过扩展字符串长度到指定长度。之后设置两个新老指针,通过双指针的方式,插入字符,达到效果。
//时间复杂度O(n)
//空间复杂度O(1)
#include <iostream>
using namespace std;
int main() {
string s;
while (cin >> s) {
int sOldIndex = s.size() - 1;
int count = 0; // 统计数字的个数
for (int i = 0; i < s.size(); i++) {
if (s[i] >= '0' && s[i] <= '9') {
count++;
}
}
// 扩充字符串s的大小,也就是将每个数字替换成"number"之后的大小
s.resize(s.size() + count * 5);
int sNewIndex = s.size() - 1;
// 从后往前将数字替换为"number"
while (sOldIndex >= 0) {
if (s[sOldIndex] >= '0' && s[sOldIndex] <= '9') {
s[sNewIndex--] = 'r';
s[sNewIndex--] = 'e';
s[sNewIndex--] = 'b';
s[sNewIndex--] = 'm';
s[sNewIndex--] = 'u';
s[sNewIndex--] = 'n';
} else {
s[sNewIndex--] = s[sOldIndex];
}
sOldIndex--;
}
cout << s << endl;
}
system("pause");
return 0;
}
收获:
- 数组填充类的问题,其做法都是先预先给数组扩容待填充后的大小,然后再从后向前进行操作。这么做有两个好处:①不用申请新数组;②从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要讲添加元素之后的所有元素向后移动的问题。
- 字符串和数组的区别,字符串是若干字符组成的有限序列,也可以理解为是一个字符数组,但是很多语言对字符串做了特殊的规定。其中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类型。
总结
要注意for循环的特殊使用,包括还有for(auto a : c)的模式,以及字符串和数组的区别。至今为止,目前解决的双指针题目有:27.移除元素、15.三数之和、18.四数之和、206.反转链表、142.环形链表II、344.反转字符串。