代码随想录day8 | LeetCode344. 反转字符串、LeetCode541. 反转字符串 II、KamaCoder54. 替换数字(第八期模拟笔试)
反转字符串
题目链接:LeetCode344. 反转字符串
自己敲
双指针法
class Solution {
public:
void reverseString(vector<char>& s) {
char *p,*q;
p=&s[0],q=&s[0];
q+=s.size()-1;
while(p<q){
swap(*p,*q);
p++;
q--;
}
}
};
class Solution {
public:
void reverseString(vector<char>& s) {
char *p=s.data(),*q=&s.back();
while(p<q){//指针小于,不是指针所指的数值
swap(*p,*q);
p++;
q--;
}
}
};
看题解
(直接用C++里的一个库函数 reverse)
如果在现场面试中,我们什么时候使用库函数,什么时候不要用库函数呢?
如果题目关键的部分直接用库函数就可以解决,建议不要使用库函数。
如果库函数仅仅是 解题过程中的一小部分,并且你已经很清楚这个库函数的内部实现原理的话,可以考虑使用库函数。
class Solution {
public:
void reverseString(vector<char>& s) {
for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
swap(s[i],s[j]);
}
}
};
总结
-
有下标的时候就不用再创建指针了,多此一举,下标即指针(vector底层是连续空间)
-
swap可以有两种实现。
- 一种就是常见的交换数值:
int tmp = s[i]; s[i] = s[j]; s[j] = tmp;
- 一种就是通过位运算:
s[i] ^= s[j]; s[j] ^= s[i]; s[i] ^= s[j];
反转字符串Ⅱ
自己敲
class Solution {
public:
string reverseStr(string s, int k) {
int begin=0,end=2*k+begin-1;
while(1){
if(s.size()-begin<k){
reverse(s,begin,s.size()-begin-1);
break;
}else if(s.size()-begin>=k&&s.size()-begin<2*k){
reverse(s,begin,begin+k-1);
break;
}else{
reverse(s,begin,begin+k-1);
}
begin+=2*k;
end+=2*k;
}
return s;
}
void reverse(string &s,int begin,int end){
while(begin<end){
swap(s[begin],s[end]);
begin++;
end--;
}
}
};
部分测试用例未通过
看题解
这种题目属于模拟
一些同学可能为了处理逻辑:每隔2k个字符的前k的字符,写了一堆逻辑代码或者再搞一个计数器,来统计2k,再统计前k个字符。
其实在遍历字符串的过程中,只要让 i += (2 * k),i 每次移动 2 * k 就可以了,然后判断是否需要有反转的区间。
因为要找的也就是每2 * k 区间的起点,这样写,程序会高效很多。
所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。
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;
}
};
如果要自己实现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)) {
// 1. 每隔 2k 个字符的前 k 个字符进行反转
// 2. 剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符
if (i + k <= s.size()) {
reverse(s, i, i + k - 1);
continue;
}
// 3. 剩余字符少于 k 个,则将剩余字符全部反转。
reverse(s, i, s.size() - 1);
}
return s;
}
};
总结
- 所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章
没找到代码错哪了
觉得下标和个数之间关系要好好理解
替换数字
题目链接:KamaCoder54. 替换数字(第八期模拟笔试)
自己敲
用了辅助空间
#include<iostream>
#include<string>
#include<vector>
using namespace std;
int main(){
string s;
cin>>s;
vector<char> result;
for(int i=0;i<s.size();i++){
if(s[i]>='a'&&s[i]<='z'){
result.push_back(s[i]);
}
if(s[i]>='0'&&s[i]<='9'){
result.push_back('n');
result.push_back('u');
result.push_back('m');
result.push_back('b');
result.push_back('e');
result.push_back('r');
}
}
for(int i=0;i<result.size();i++){
cout<<result[i];
}
return 0;
}
看题解
不使用额外辅助空间
首先扩充数组到每个数字字符替换成 “number” 之后的大小。
例如 字符串 “a5b” 的长度为3,那么 将 数字字符变成字符串 “number” 之后的字符串为 “anumberb” 长度为 8。
如图:
然后从后向前替换数字字符,也就是双指针法,过程如下:i指向新长度的末尾,j指向旧长度的末尾。
为什么要从后向前填充,从前向后填充不行么?
从前向后填充就是O(n^2)的算法了,因为每次添加元素都要将添加元素之后的所有元素整体向后移动。
其实很多数组填充类的问题,其做法都是先预先给数组扩容带填充后的大小,然后在从后向前进行操作。
这么做有两个好处:
- 不用申请新数组。
- 从后向前填充元素,避免了从前向后填充元素时,每次添加元素都要将添加元素之后的所有元素向后移动的问题。
#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;
}
}
总结
-
数组填充类的问题:先预先给数组扩容带填充后的大小,然后在从后向前进行操作
-
C++中字符串和字符数组
- 在C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束,就不用’\0’来判断是否结束。
- 那么vector< char > 和 string 在基本操作上没有区别,但是 string提供更多的字符串处理的相关接口,例如string 重载了+,而vector却没有。