题目链接:反转字符串
视频讲解:字符串基本操作
用双指针法对reverse函数进行重写。
// 时间复杂度: O(n)
// 空间复杂度: O(1)
class Solution {
public:
void reverseString(vector& s) {
for (int i = 0, j = s.size() - 1; i < s.size()/2; i++, j--) {
swap(s[i],s[j]);
}
}
};
题目链接:反转字符串II
视频讲解:字符串进阶操作
主要理清反转规则,代码就不会写的那么冗长。在遍历字符串时,让i每次移动2 * k步,对移动的区间进行翻转就可以了。
// 时间复杂度: O(n)
// 空间复杂度: O(1)
class Solution {
public:
string reverseStr(string s, int k) {
for (int i =0; i < s.length(); i += 2 * k)
{
if (i + k <= s.length()) //2k个字符的qia前k个字符进行反转
{
reverse(s.begin() + i, s.begin() + i + k);
}
else // 不足k个字符,剩余的全部反转
{
reverse(s.begin() + i, s.end());
}
}
return s;
}
};
题目链接:替换数字
此题依然用双指针的方法。首先是要把字符串扩容到数字换成number后的大小,用双指针时应该从后向前替换数字字符。填充number也要从后向前开始,若是从前向后每次添加元素都要把添加元素之后的元素向后移,增加了时间复杂度。
很多数组填充类的问题,其做法都是先预先给数组扩容,然后再从后向前进行操作。这样就不用申请新的数组,同时避免了从前向后添加元素时,还要将填充后的元素向后移动。
// 时间复杂度:O(n)
// 空间复杂度:O(1)
#include
#include
using namespace std;
int main()
{
string s;
while (cin >> s)
{
int count = 0; // 统计字符串中数字的个数,方便扩容
int len = s.size();
for (int i = 0; i < len; i++)
{
if (s[i] >= '0' && s[i] <= '9')
{
count++;
}
}
// 将字符串扩容
s.resize(s.size() + 5 * count);
for (int i = s.size() -1 , j = len - 1; j < i; j--, i--)
{
if (s[j] > '9' || s[j] < '0') // 不是数字往字符串后面放
{
s[i] = s[j];
}
else // 从后往前填充
{
s[i] = 'r';
s[i - 1] = 'e';
s[i - 2] = 'b';
s[i - 3] = 'm';
s[i - 4] = 'u';
s[i - 5] = 'n';
i -= 5;
}
}
cout << s << endl;
}
}
题目链接:翻转字符串里的单词
视频讲解:字符串复杂操作拿捏了!
要先考虑好怎么去翻转字符串里的单词,思考一下,我们可以先将整个字符串反转,此时单词是个反的,然后再逐个反转单词。
方法
1、移除多余的空格;
2、反转整个字符串;
3、逐个反转单词;
移除多余的空格也有很多细节需要考虑,我们可以多创建一个字符串,将原字符串里需要的元素放入新字符串,但这样空间复杂度就增加了。也可以在保持空间复杂度为O(1)同时,删去多余的空格,这就要用到双指针法了,与删除数组中的元素类似。
我们可以先删除字符串中的所有空格,然后再在相邻单词之间加一个空格,这样就不需要判断原字符串中哪些空格需要删,哪些不需要。
学习总结:
1、重温了反转字符串;
2、移除元素,双指针很好用;
3、本题移除元素后要更新字符串的大小;
// 时间复杂度: O(n)
// 空间复杂度: O(1) 或 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 reverseExtraSpaces(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] != ' ') // 获取单词,遇到空格表示一个单词结束,出循环后先删空格再添加一个空格
{
s[slow++] = s[i++];
}
}
}
s.resize(slow); // 更新字符串的大小
}
string reverseWords(string s) {
reverseExtraSpaces(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;
}
};
题目链接:右旋字符串
此题需要在不增加空间复杂度的基础上完成,即不申请额外的空间,只能在本串上操作。
做了上一题,我么可以从中受到启发,怎么反转子串。我们先可以把整个字符串全部反转,反转后字母顺序也反转了。这时就可以把字符串分为两部分,前一部分是需要右旋的位数,这几位数本来在后面,整个字符串反转后到了前面但是顺序变了,同理后一部分字符的顺序也变了。我们分别再反转这两部分就可以得到右旋字符串了。
整体反转后
子串反转
#include
#include
#include
using namespace std;
int main()
{
int k;
string s;
cin >> k;
cin >> s;
reverse(s.begin(), s.end());
reverse(s.begin(), s.begin() + k);
reverse(s.begin() + k, s.end());
cout << s << endl;
}