【算法练习Day7】反转字符串&&替换空格&&反转字符串中的单词&&左旋转字符串

在这里插入图片描述

​📝个人主页:@Sherry的成长之路
🏠学习社区:Sherry的成长之路(个人社区)
📖专栏链接:练题
🎯长路漫漫浩浩,万事皆有期待

反转字符串

344. 反转字符串 - 力扣(LeetCode)
在这里插入图片描述

这道题思路并不难,甚至可以直接reverse调用库函数来一步到位(但是我之前并不知道这个库函数)reverse(开始地址,结束地址),可以将一个字符串从前到后依次反转,也就是第一个元素跑到最后一位,第二位元素跑到倒数第二个位置上,以此类推,不过直接使用reverse就失去了做题的意义了。

于是有了另一种思路:用双指针的思路,一层遍历,将字符串反转,与其说是换一种思路,倒不如说是reverse库函数的一种实现,代码如下

class Solution {
public:
    void reverseString(vector<char>& s) 
    {
        int l=s.size();
        for(int i=0,j=l-1;i<l/2;i++,j--)
        swap(s[i],s[j]);
    }
};

反转字符串 II

541. 反转字符串 II - 力扣(LeetCode)
在这里插入图片描述

反转字符串II这题,乍一看好像真的比上一道要难不少,要求很多,不知道该如何下手,但是只要知道思路了,代码很容易实现,题目大意是给一个字符串和一个整数k,每隔2k个数字,就反转前k个字符,如果剩余字符不足2k但是大于或等于k个反转前k个剩余保持不变(等待再次判断),如果小于k个全部反转。这道题的精髓在于控制一个变量,使它一次走2k步,在循环中用if来调整其余的判断部分,这样可以使代码变得富有规律,且代码不易冗余。

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;
    }
};

在代码中我们可以看到,进入循环我们直接判断剩余的元素个数满足哪一个条件,请注意,字符反转与否,与剩余字符有着很大的关联!不要将题目理解错误,我们在if里面包含了上述题目中,剩余字符很多(可以继续遍历2k个)和剩余字符大于k但不足2k个两种情况,也就是说以上两种情况,都是可以正常反转k个,如果到了else那就说明不足k个了,只能将后面元素全部反转了。理清思路了,代码还是很好写的。

替换空格

替换空格 - 牛客

请实现一个函数,把字符串 s 中的每个空格替换成"%20"。
示例 1: 输入:s = “We are happy.”
输出:"We%20are%20happy.

这道也是之前在牛客上做过的一道题,只是忘记了思路。后来看了题解想了起来,题目思路不算难,但是没有做过基本很难做出来,题目要求是给你一个字符串,将字符串中空格位置区域替换成%20的符号,思路是遍历一遍字符串查找有多少个空格,在字符串后面补充空格数*2,因为本来有一个空格,所以只需乘以2就足够了,然后用双指针方法,一个指针指向新空间末尾,一个指针指向字符串末尾,遇到字符就复制过来,遇到空格填充%20,整体代码没有难度,只是思路有点难想。

class Solution {
public:
    string replaceSpace(string s) {
        int len1=s.size();int count=0;// 统计空格的个数
        for(int i=0;i<len1;i++)
        {
            if(s[i]==' ')
            count++;
        }
        // 扩充字符串s的大小,也就是每个空格替换成"%20"之后的大小
        s.resize(s.size()+2*count);
        int len2=s.size();
         // 从后先前将空格替换为"%20"
        for(int i=len2,j=len1;i>j;i--,j--)
        {
            if(s[j]==' ')
            {
                s[i]='0';
                s[i-1]='2';
                s[i-2]='%';
                i-=2;
            }
            else
            s[i]=s[j];
        }
        return s;
    }
};

时间复杂度: O(n)
空间复杂度: O(1)

反转字符串中的单词

151. 反转字符串中的单词 - 力扣(LeetCode)
在这里插入图片描述

这道题目leetcode给出中等,这道题实际上包含了字符串的很多操作,十分繁琐,说是困难题应该也不为过,题目要求将给定字符串去除除了每个单词之间的空格之外的所有空格,并且将整个字符串反转,这个字符串的前后和中间都有可能出现多个空格,这是难处理的点,然后还要将字符串内容全部反转(不是将每个字符反转,而是将单词反转)。

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 removeExtraSpace(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);//slow的大小即为去除多余空格后的大小。
    }
    string reverseWords(string s) {
        removeExtraSpace(s);//去除多余空格,保证单词之间之只有一个空格,且字符串首尾没空格。
        reverse(s,0,s.size()-1);
        int start=0;//removeExtraSpaces后保证第一个单词的开始下标一定是0。
        for(int i=0;i<=s.size();i++)
        {
            if(i==s.size()||s[i]==' ')
            {//到达空格或者串尾,说明一个单词结束。进行翻转。
                reverse(s,start,i-1);//翻转,注意是左闭右闭 []的翻转。
                start=i+1;//更新下一个单词的开始下标start
            }
        }
        return s;
    }
};

最上面的部分,是自定义的交换字符的函数,实现的是前闭后闭的思路,后面的代码主要是删除多余空格,第一个while让fast指针向后走,主要是为了删除开头多余的空格,for循环判断fast指针如果不在第一个位置,说明单词要直接插进去,如果大于0,且前面有空格当前所在位置也有空格,那么说明空格多出来了,需要删掉,这里我们的处理方法与这篇博客中的 移除元素 方法一致。

之后最关键的一步也就是反转字符串,第一次先将字符串整体反转,这样就将整个字符串的所有字符全部反转了,也就是说最后一个字符和第一个字符交换,以此类推。这样最后一个单词的反转就到了第一个位置,倒数第二单词的反转就到了第二个位置,这样的反转已经完成了题目要求的一半了,之后我们的思路就是将每个单词再反转,如何判断呢?

当我们遍历到空格那就说明遍历到一个单词的最后一个字符了,那么我们将这个单词整体反转,如果遍历到最后一个单词,当变量i走到字符最后一个位置说明该反转最后一个单词了,这里的start尤为重要,它标识了一个单词反转的起始位置,而i标识了这个单词的结束位置,遵循先整体反转再局部反转的原则。

时间复杂度: O(n)
空间复杂度: O(1)

左旋转字符串

左旋转字符串 - 牛客
在这里插入图片描述

左旋字符串,就是将该字符串的左边k个字符旋转到字符串后面但是并不将任何部分反转,只不过是将字符调整了位置。

不限制空间复杂度的情况下,最简单的方法就是新开字符串,将后面的部分先复制到新字符串里,紧接着又将前面的部分取下复制到新字符串里就完成了。

class Solution {
public:
    string LeftRotateString(string str, int n) {
        int len=str.size();
        if(len==0)
        {
            return "";
        }
        n%=len;
        string result;
        for(int i=n;i<str.size();i++)
        result.push_back(str[i]);
        for(int i=0;i<n;i++)
        result.push_back(str[i]);
        return result;
    }
};

如果要求空间复杂度呢?不允许开辟新内存,只能在原字符串操作呢?

我们先将前半部分反转,再将后半部分反转,最后再反转整体,是不是很神奇?可以自行模拟下

class Solution {
public:
    string LeftRotateString(string str, int n) {
        int len=str.size();
        if(len==0)
        {
            return "";
        }
        n%=len;
        reverse(str.begin(),str.begin()+n);
        reverse(str.begin()+n,str.end());
        reverse(str.begin(),str.end());
        return str;
    }
};

总结:

今天我们完成了字符串章节的几道题,与之前的一些题有相似之处,相关的思想需要多复习回顾。接下来,我们继续进行算法练习·。希望我的文章和讲解能对大家的学习提供一些帮助。

当然,本文仍有许多不足之处,欢迎各位小伙伴们随时私信交流、批评指正!我们下期见~

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Sherry的成长之路

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值