C++ 中直接用reverse
库函数就可以解决问题。但是需要注意的是,在leetcode面试时,遇到直接就能用库函数解决的问题不要用库函数;如果库函数只是解题步骤中的一小步,而且对库函数的内部实现原理比较熟悉时,可以使用。
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]);
}
}
};
注意这里的swap
是库函数,而且交换的实现也很简单,直接交换也可,位运算下通过连续的异或也可。
s[i] ^= s[j];
s[j] ^= s[i];
s[i] ^= s[j];
花式反转,但仍然可以用双指针来解决。我们已经在上一题实现了反转字符串的函数,这里可以用,也可以直接用库函数。分析题意,是每到第 2k 个数时,反转前 k 个,因此我们可以直接一次 2k 步地向前遍历,不必一个一个地遍历,代码会简洁很多。
所以当需要固定规律一段一段去处理字符串的时候,要想想在在for循环的表达式上做做文章。
class Solution{
public:
// **** 注意这里要用指针,保证会改变传入的字符串,而不是只改变形参 ****
void reverse(string& s, int begin, int end){
int len = end - begin + 1;
for(int i = begin, j = end; i < len / 2; i++, j--){
swap(s[i], s[j]);
}
}
string reverseStr(string s, int k){
// i一次前进2k,每一次都是字符串新起点
for(int i = 0; i < s.size(); i += (2 * k)){
// [i,i+k)左闭右开区间内的字符串,一共k个字符
if(i + k <= s.size()){
reverse(s, i, i + k - 1);
}
else{
reverse(s, i, s.size() - 1);
}
}
return s;
}
};
class Solution{
public:
string reverseStr(string s, int k){
for(int i = 0; i < s.size(); i += (2 * k)){
if(i + k <= s.size()){
reverse(s.begin() + i, s.begin() + i + k);
}
else{
reverse(s.begin() + i, s.end());
}
}
return s;
}
};
class Solution{
public:
string reverseStr(string s, int k){
int pos = 0;
while(pos < s.size()){
if(pos + k <= s.size())
reverse(s.begin() + pos, s.begin() + pos + k);
else reverse(s.begin() + pos, s.end());
pos += (2 * k);
}
return s;
}
};
其实用暴力解法是可以做的,我们知道新输出字符串的长度,遇到数字就将后面的字符向后移动,再写入number,但是这样做时间复杂度太高 O(n^2),可以采用双指针换个方向遍历来解决问题,太妙了!!!
#include
using namespace std;
int main(){
string s;
while(cin >> s){
int count = 0;
int Oldlen = s.size();
for(int i = 0; i < Oldlen; i++){
if(s[i] <= '9' && s[i] >= '0'){
count++;
}
}
s.resize(Oldlen + count * 5);
int Newlen = s.size();
for(int i = Oldlen - 1, j = Newlen - 1; i >= 0; i--, j--){
if(s[i] > '9' || s[i] < '0'){
s[j] = s[i];
}
else{
s[j] = 'r';
s[j - 1] = 'e';
s[j - 2] = 'b';
s[j - 3] = 'm';
s[j - 4] = 'u';
s[j - 5] = 'n';
j -= 5;
}
}
cout << s << endl;
}
}
字符串与数组的区别,在 python 中字符串是不可变类型,如果要修改字符串,需要将字符串转化为list,然后
''.join()
。在 C++ 中当字符串存入数组时,必须以 \0 来标志结束。而 string 类型则提供了其他接口来获得字符串信息和操作字符串,其实基本操作上vector
和string
差不多,只是 string 提供了更多字符串处理的接口。
分析问题:其实有split
这样的库函数,定义一个新字符串,然后通过split
分出单词,将单词逆序放进新字符串中。这里我们追求不使用辅助空间,可以将这个问题分解为:
erase
。void removeExtraSpaces(string& s){
// 删除字符串中重复出现的空格
for(int i = s.size() - 1; i > 0; i--){
if(s[i] == s[i - 1] && s[i] == ' '){
s.erase(s.begin() + i);
}
}
// 删除开头的空格
if(s[0] == ' ') s.erase(s.begin());
if(s[s.size() - 1] == ' ') s.erase(s.begin() + s.size() - 1);
}
这种写法看上去是 O(n),但其实erase
函数是 O(n) 的(移除元素),这个算法其实是 O(n^2) 的。而且这种erase
的写法要注意移除一个元素之后索引下标会有变化,所以不能从前向后移除,那样下标就乱了。直接采用移除元素中的双指针法进行这一步的处理,时间复杂度 O(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 removeExtraSpaces(string& s){
int fast = 0, slow = 0;
for(; fast < s.size(); fast++){
if(s[fast] != ' '){
if(slow != 0){
s[slow++] = ' ';
}
while(fast < s.size() && s[fast] != ' ') s[slow++] = s[fast++];
}
}
s.resize(slow);
}
string reverseWords(string s){
removeExtraSpaces(s);
reverse(s, 0, s.size() - 1);
int start = 0;
for(int i = 0; i <= s.size(); i++){
if(i == s.size() || s[i] == ' '){
reverse(s, start, i - 1);
start = i + 1;
}
}
return s;
}
};
先贴一份自己的,每次将最前面未确定的字符与后 k 个字符做交换,最终实现整体的移动。
#include
#include
using namespace std;
int main(){
string s;
int k = 0;
cin >> k;
cin >> s
int start = 0;
int len = s.size() - k; // 当前剩余需要交换的字符数目
while(len > 0){
for(int i = start, j = s.size() - k; i < start + k; i++, j++){
swap(s[i], s[j]);
}
len -= k;
start += k;
}
cout << s << endl;
}
题解中给的方法是从今天翻转字符串的题目中发散而来的,我们可以把问题转换成这样:一个字符串分为 len-n 和 n 两段,将后面那段放到前面去。这就和刚刚的翻转字符串中的单词很像了,都是都是翻转一段字符的顺序:先翻转整个字符串,再分别翻转两段字符串。
#include
#include
using namespace std;
int main(){
string s;
int k = 0;
cin >> k;
cin >> s;
int len = s.size();
reverse(s.begin(), s.end());
reverse(s.begin(), s.begin() + k);
reverse(s.begin() + k, s.end());
cout << s << endl;
}
反转的顺序也可以很灵活,先翻转局部,再翻转整体也可,但需要注意区间的起止点。