【C++】string OJ练习(一)

简介: 【C++】string OJ练习

我们来看几道string相关的OJ,来练习一下string的使用。

1. 仅仅反转字母

题目链接: link

我们一起来看一下题:

d110889707c847e5a534ef1e74a883eb.png

思路分析

我们来分析一下题目,这道题让我们干什么呢?

给我们一个字符串,该字符串中有英文字符也有非英文字符,要求我们去反转字符串中的所有英文字母,非英文字母的字符位置不动。

那是不是很简单啊,左右两个指针分别指向首尾,然后依次向中间移动寻找英文字母,找到后停下来,然后两个指针指向的英文字母进行交换,接着继续向中间移动,两者相遇结束。(是不是跟一趟快排的逻辑有点像啊)

代码实现

class Solution {
public:
    bool isletter(char x)
    {
        if((x>='a'&&x<='z')||(x>='A'&&x<='Z'))
            return true;
        return false;
    }
    string reverseOnlyLetters(string s) {
        int begin=0;
        int end=s.size()-1;
        while(begin<end)
        {
            while((begin<end)&&!isletter(s[begin]))
                begin++;
            while((begin<end)&&!isletter(s[end]))
                end--;
            swap(s[begin],s[end]);
            begin++;  
            end--;
        }
        return s;
    }
};

代码呢也比较简单,相信大家都能看懂,就不过多解释了。


d171a98c98bd44ce866a957083e2644a.png

2. 字符串中的第一个唯一字符

链接: link

992dd157b4ce469992746bdb80f3c724.png

题目分析

题目让我们找出字符串中第一个不重复的字符,那我们最容易想到的就是暴力求解,从头到尾遍历字符串,依次拿每一个字符与其他字符进行比较,如果没有与之重复的则当前字符就是要找的字符,返回其下标,有重复的就不是,继续看下一个,最终一个也没找到就返回-1。

当然这样的时间复杂度就是O(N^2)

那有没有好一点的方法呢?

🆗,其实呢我们可以考虑用计数排序的思想去搞:

题目说了只包含小写字母

0284658f9672415baff857ca145023d1.png

所以字符串中字符的范围就是【a,z】,那我们就可以创建一个大小为26的整型数组,然后用一个相对映射去统计每个字母的出现次数,a就映射到下标为0的位置,b就映射到下标为1的位置,依次类推。

那怎么让这些字母映射到对应的位置呢?

减去’a’得到的值是不是就是它们映射的位置啊,然后遍历字符串,每个字母映射的值是几,就让下标为几的元素++,初值全为0,这样遍历过后每个字母出现的次数就统计出来了。(下标0的元素的值就是a出现的次数,1位置就是b出现的次数…)

但是现在有一个问题,那就是出现一次的字母可能不止一个,我们怎么判断那个是第一个只出现一次的字母呢?

🆗,这里我们不要去遍历统计次数的数组,还是从前往后去遍历字符串,然后看哪个字母的次数是1,第一个是1的就是第一个只出现一次的字母。

代码实现

class Solution {
public:
    int firstUniqChar(string s) {
        int count[26]={0};
        for(auto e:s)
        {
            count[e-'a']++;
        }
        for(int i=0;i<s.size();i++)
        {
            if(count[s[i]-'a']==1)
                return i;
        }
        return -1;
    }
};

大家结合上面的分析再看一看代码,相信就能理解了。

576e5740189949349e090a4661cc63e1.png

3. 《剑指offer》——替换空格

接下来我们来看一道《剑指offer》于string相关的题目——替换空格


5faf86020d3a44ed9d1a120bc3fdb087.png

解法一:寻找替换

思路分析

大家思考一下这道题可以怎么解?

是不是可以考虑用find+replace搞啊。

用find找的字符串中的所有空格,然后用replace将其替换成%20不就行了嘛。

代码实现

我们来实现一下代码:

class Solution {
public:
    string replaceSpace(string s) {
        size_t pos=s.find(' ');
        while(pos!=string::npos)//find找不到返回npos
        {
            s.replace(pos,1,"%20");
            pos=s.find(' ');
        }
        return s;
    }
};

f62debad00494278b8fe625d38f290c7.png

🆗,就通过了。

优化

那大家看一下,我们刚才上面那样写好吗,或者说有没有可以优化的地方?

是可以做一些优化的。

4edbdf03eecc4c61ba62c738dc12acab.png

来看find是不是可以指定开始查找的位置啊,如果我们不传pos的话它默认是从起始位置开始查找的,但是这里我们要查找所有的空格,并且对它们进行替换,那第一个空格被替换之后,我们往后查找第二个的时候,还有必要从头开始找吗,是不是就可以从替换之后的尾部开始往后找啊,这样效率是不是就高了一点会。

所以我们可以这样改进一下:

294d067404a54f178962dd356f51e94e.png

80a4df97196248ad8a01b4444fb9ac55.png

那大家再想,还可以再优化吗?


其实还有一个地方可以做一些优化,大家想,我们这里replace是把空格替换成%20,这样使用的空间是不是多了,那replace在替换的过程中是不是有可能空间不够进行扩容啊,那有没有什么办法可以避免replace的过程中可能会去频繁的扩容(扩容是有消耗的,特别是异地扩)。

🆗,我们是不是可以计算出需要多少空间,然后提前把空间开好啊。那大家说,这里应该用什么?

resize还是 reserve,reserve可以改变容量帮我们开空间,而resize除了开空间还可以初始化,但是这里有必要对开好的空间进行初始化吗?

是不是没必要啊,所以我们用reserve就行了。

那需要多少空间呢,空格替换成%20,所以每个空格多两个空间,那我们可以统计一下空格数,然后提前把空间开好

写一下代码:

class Solution {
public:
    string replaceSpace(string s) {
        int count=0;
        for(auto e:s)
        {
            if(e==' ')
                count++;
        }
        s.reserve(s.size()+2*count);
        size_t pos=s.find(' ');
        while(pos!=string::npos)
        {
            s.replace(pos,1,"%20");
            pos=s.find(' ',pos+3);
        }
        return s;
    }
};

5510b5db707646c89ae2492a75349c34.png

解法二:空间换时间

那除了上面那种方法,其实还可以考虑另一种思路:


思路分析

我们可以创建一个新的string对象,然后遍历原串,不是空格,就直接+=到新串,是空格,就把"%20"+=到新串。

这样的好处是不需要像上面那样挪动数据了,只不过多开了一点空间。


代码实现

这个代码就非常简单:

class Solution {
public:
    string replaceSpace(string s) {
        string news;
        for(auto e:s)
        {
            if(e!=' ')
                news+=e;
            else
                news+="%20";
        }
        return news;
    }
};

c14c778975554624908a839ea427c400.png

当然这种方法在+=的过程中也有可能会扩容,所以我们也可以把reserve那一步加上。

目录
相关文章
|
1月前
|
C++ 容器
|
1月前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
21 1
|
1月前
|
C++ 容器
|
1月前
|
C++ 容器
|
1月前
|
存储 C++ 容器
|
1月前
|
安全 C语言 C++
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
【C++篇】探寻C++ STL之美:从string类的基础到高级操作的全面解析
34 4
|
1月前
|
存储 编译器 程序员
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
【C++篇】手撕 C++ string 类:从零实现到深入剖析的模拟之路
63 2
|
1月前
|
编译器 C语言 C++
【C++】C++ STL 探索:String的使用与理解(三)
【C++】C++ STL 探索:String的使用与理解
|
1月前
|
存储 编译器 C++
【C++】C++ STL 探索:String的使用与理解(二)
【C++】C++ STL 探索:String的使用与理解
|
1月前
|
编译器 C语言 C++
【C++】C++ STL 探索:String的使用与理解(一)
【C++】C++ STL 探索:String的使用与理解