🔥博客主页: A_SHOWY
🎥系列专栏:力扣刷题总结录 数据结构 云计算 数字图像处理
344.反转字符串 | e | swap应用+双指针 |
541.反转字符串Ⅱ | m | 可以自己定义函数也可以直接reverse |
151.反转字符串中的单词 | m | 局部翻转加全局翻转 |
卡码网 替换数字 | m | 找到数字的数量双指针应用 |
卡码网 右旋字符串 | e | 局部翻转加全局翻转应用 |
(1)344.反转字符串
344. 反转字符串https://leetcode.cn/problems/reverse-string/
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]);
}
}
};
1.本题目是最经典最基础的双指针法,可以直接用到swap库函数即可
2.在判断i的取值范围的时候,如果对于边界无法把握是否有等于号,想几个特殊的例子即可
(2)541.反转字符串Ⅱ
541. 反转字符串 IIhttps://leetcode.cn/problems/reverse-string-ii/方法一:使用库函数reverse
class Solution {
public:
string reverseStr(string s, int k) {
for(int i =0; i < s.length();i += (2*k))
{
if(i+k<s.length()) reverse(s.begin() + i,s.begin() + i + k);
else reverse(s.begin() + i, s.end());
}
return s;
}
};
可以使用reverse库函数完成注意只需要注意循环的停止条件i += 2k,每隔2k跳一次,如果够k就局部前k个翻转,如果不够,就全部翻转.
方法二:自己定义翻转函数
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.length(); i += (2 * k))
{
if(i + k <= s.length()) reverse(s,i,i+k-1);
else{
reverse(s,i,s.length()-1);
}
}
return s;
}
};
1.使用string&
作为函数参数表示传递的是字符串的引用,使用string&
作为函数参数允许直接操作调用者提供的字符串,而不是复制一份新的字符串,这样可以提高效率并节省内存。
2.注意使用自己定义的翻转函数的时候,下标 = 第几位 - 1
3.这种方法就是在第一题的基础上一个应用,使用双指针构造这种全局的翻转
(3)151.反转字符串中的单词
151. 反转字符串中的单词https://leetcode.cn/problems/reverse-words-in-a-string/
对于删除多于的空格的操作,底下这种操作其实是O(n^2)的,erase时间复杂度是O(n),在for循环里所以为n方。
void removeextrething(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[s.size()-1] == ' '&& s.size() > 0){
s.erase(s.begin() + s.size() - 1);
}
//删除字符串最前面的空格
if(s[0] ==' ' && s.size() > 0){
s.erase(s.begin());
}
}
那么使用双指针法来去移除空格,最后resize(重新设置)一下字符串的大小,就可以做到O(n)的时间复杂度。
void removeExtraSpaces(string& s) {
int slowIndex = 0, fastIndex = 0; // 定义快指针,慢指针
// 去掉字符串前面的空格
while (s.size() > 0 && fastIndex < s.size() && s[fastIndex] == ' ') {
fastIndex++;
}
for (; fastIndex < s.size(); fastIndex++) {
// 去掉字符串中间部分的冗余空格
if (fastIndex - 1 > 0
&& s[fastIndex - 1] == s[fastIndex]
&& s[fastIndex] == ' ') {
continue;
} else {
s[slowIndex++] = s[fastIndex];
}
}
if (slowIndex - 1 > 0 && s[slowIndex - 1] == ' ') { // 去掉字符串末尾的空格
s.resize(slowIndex - 1);
} else {
s.resize(slowIndex); // 重新设置字符串大小
}
}
那么比较完整的代码如下:
class Solution {
public:
void removeExtrathing(string& s)//第一个函数删除多余空格
{
int slow = 0;
for(int i = 0; i< s.size(); ++i){
if(s[i] != ' ')//找出不是空格的开始操作
{
if(slow != 0){//slow不等于0就不是第一个单词
s[slow++] = ' ';
}
while(i < s.size()&& s[i] != ' ') {
s[slow++] = s[i++];
}
}
}
s.resize(slow);
}
void reverse(string& s,int left,int right)//第二个函数反转字符串
{
for(int i = left,j = right;i < j;i++,j--)
{
swap(s[i],s[j]);
}
}
string reverseWords(string s) {
removeExtrathing(s);
reverse(s,0,s.size() - 1);//先把整体反转
int start = 0 ;
for(int i = 0; i <= s.size();i++)//注意等于的情况
{
if(s[i] == ' ' || i == s.size())//有空格或者达到串尾说明一个单词结束
{reverse(s,start,i-1);
start = i+1;}
}
return s;
}
};
1.可以看到删除多余的空格可以一起讨论没必要分开讨论,当不是空格的时候,开始操作,可以联想到我们以前在学习一道移除元素的题目的思路,如果不清楚可以看这篇文章,让快指针走,遇到不要的元素就跳过,遇到要的就给慢指针。力扣刷题总结 数组(1)-CSDN博客https://blog.csdn.net/showy0/article/details/134212692?spm=1001.2014.3001.5501
if(s[i] != ' ')//找出不是空格的开始操作
2.注意这里手动加入空格,当出现slow不等于0的时候就是不是第一个单词,给个空格,放到i传给慢指针语句的后面
if(slow != 0){//slow不等于0就是不是第一个单词
s[slow++] = ' ';
3.第二个函数就是反转字符串和我们前面练习的两个题一模一样是一个应用,可以当作模板直接记住。
4.主函数的操作顺序是,先删除多余的空格,整体字符串反转,然后当出现空格或者最后的时候,单个单词的反转,整个题目结束。
(4)卡码网 替换数字
题目页面 (kamacoder.com)https://kamacoder.com/problempage.php?pid=10641.整个题目的做题思路应该比较清晰,首先第一步是找到一共有多少个数字(all)需要替换,第二步是重新给字符串新的空间(oldsize+ 5 * all),然后进行双指针,快指针指向新的字符串的末尾,慢指针指向旧的字符串的末尾,同时向前移动。此题的关键是双指针的应用。
2.至于为什么不指向字符串的开头,因为如果在开头,在遍历的过程中添加元素需要所有元素跟着移动,而且不用申请新的数组。
#include<iostream>
using namespace std;
int main()
{
string s;
int all = 0;
while(cin>>s){
int oldSize = s.size();
//先统计一下数字数量
for(int i = 0;i< s.size();i++)
{
if(s[i] >= '0' && s[i] <= '9'){
all++;
}
}
s.resize(s.size() + 5 * all);
int newSize = s.size();
for(int i = oldSize-1,j = newSize-1;i < j;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;
}
补充:关于字符串和数组,字符串是若干字符组成的有限序列,也可以理解为是一个字符数组。C++中,提供一个string类,string类会提供 size接口,可以用来判断string类字符串是否结束。例如:
string s = "abd";
for(int i = 0; i < s.size();i++){
}
(5)卡码网 右旋字符串
题目页面 (kamacoder.com)https://kamacoder.com/problempage.php?pid=10651.这个题目其实就是一个整体和局部的反转操作的组合,那么如果先整体进行反转,再分成两部分局部进行反转的话就可以实现
2.需要注意的是
s.begin() + n
表示容器s
中第n
个元素的迭代器(即从第n+1
个元素开始的位置)。s.end()
表示容器s
的末尾迭代器。- 在C++的STL中,
reverse()
函数中的第二个参数表示的是翻转操作的结束位置的下一个元素的位置。
方法一:先整体后局部反转
#include<iostream>
#include<algorithm>//reverse
using namespace std;
int main(){
int n;
string s;
cin>>n;
cin>>s;
int len = s.size();//先获取一下字符串长度
reverse(s.begin(),s.end());
reverse(s.begin(),s.begin() + n);
reverse(s.begin() + n,s.end());
cout<< s<<endl;
}
当然也可以先局部反转,然后再全局反转,但是这时要注意的是,前面部分反转的应该是len - n;后面反转n个,最后再全部反转
方法二:先局部后整体反转
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
int n;
string s;
cin >> n;
cin >> s;
int len = s.size(); //获取长度
reverse(s.begin(), s.begin() + len - n); // 先反转前一段,长度len-n ,注意这里是和版本一的区别
reverse(s.begin() + len - n, s.end()); // 再反转后一段
reverse(s.begin(), s.end()); // 整体反转
cout << s << endl;
}
拓展:左旋转
那么假如我们要实现左翻转,其实思路是一样的,先整体后局部或者先局部后整体,无非是旋转区间的区别,思路比较简单的做法是先局部后整体,如下
#include<iostream>
#include<algorithm>
using namespace std;
int main() {
int n;
string s;
cin >> n;
cin >> s;
int len = s.size(); //获取长度
reverse(s.begin(), s.begin() + n); // 反转第一段长度为n
reverse(s.begin() + n, s.end()); // 反转第二段长度为len-n
reverse(s.begin(), s.end()); // 整体反转
cout << s << endl;
}
那么字符串部分翻转以及翻转的应用的经典题目总结完毕了,下一篇总结关于KMP方法的具体解析和应用题目。