五、一些经典的力扣题
1.仅仅反转字母
题目链接:仅仅反转字母
这道题思路很简单,我们采用双指针,利用快速排序的思路即可。isalpha()函数是判断一个字符是否为英文字母,若是返回真,否则返回假
class Solution { public: string reverseOnlyLetters(string s) { int end=s.size()-1; int begin=0; while(begin<end) { while((begin<end)&&(!isalpha(s[begin]))) { begin++; } while((begin<end)&&(!isalpha(s[end]))) { end--; } swap(s[begin],s[end]); begin++; end--; } return s; } };
2.字符串相加
题目链接:https://leetcode.cn/problems/add-strings/
对于这道题,我们的思路是逐位相加。从末尾开始一步一步相加。一开始让进位端为0,然后将相加端都给提取出来,让这三个相加,得到一个数,然后考虑进位,然后将字符给一位一位加上去,最后逆置字符串即可。
class Solution { public: string addStrings(string num1, string num2) { int end1=num1.size()-1; int end2=num2.size()-1; string s; int carry=0; while(end1>=0||end2>=0) { int val1= (end1>=0)?num1[end1]-'0':0; int val2= (end2>=0)?num2[end2]-'0':0; end1--; end2--; int ret=val1+val2+carry; carry=ret/10; ret=ret%10; s+=ret+'0'; } if(carry>0) { s+='1'; } reverse(s.begin(),s.end()); return s; } };
当然我们也可以采用头插的方式,由于库中并没有给出头插的方式,因为头插的效率太低,string的底层是一个顺序表,我们知道对于顺序表更喜欢尾插。虽然没有头插,但是提供了insert的接口
我们使用第六个成员函数即可。将一个字符插在某个迭代器之前
class Solution { public: string addStrings(string num1, string num2) { int end1=num1.size()-1; int end2=num2.size()-1; string s; int carry=0; while(end1>=0||end2>=0) { int val1= (end1>=0)?num1[end1]-'0':0; int val2= (end2>=0)?num2[end2]-'0':0; end1--; end2--; int ret=val1+val2+carry; carry=ret/10; ret=ret%10; //s+=ret+'0'; s.insert(s.begin(),ret+'0'); } if(carry>0) { //s+='1'; s.insert(s.begin(),'1'); } //reverse(s.begin(),s.end()); return s; } };
虽然两种方式都可以,但是综合来看,第一种的效率更佳,总时间复杂度为O(N),第二种的总时间复杂度为O(N2)。
六、String其他接口
1.operator[]操作符
这个操作符,在文章我前面已经介绍过,这里在此简单的介绍一下
这个函数可以去访问string某个下标的元素,他有两个函数重载,一个是加了const修饰的,一个是未修饰的,函数在使用时候会自动匹配最合适的一个函数来进行调用
2.at
at其实与operator[]几乎是一样的。
at和operator[]唯一的不同之处就在于对于越界的检查是不一样的
当发生越界的时候,at是抛异常的方式,operator[]则是断言的方式
即at比较温柔一点,operator[]比较暴力
3.assign
assign这个接口的功能是赋值,他有以下的函数重载
如下是他与append的对比
4.insert
insert顾名思义就是在某个位置插入,他有如下的函数重载:
这些函数的功能也是比较明确简单的
如下代码是部分函数的演示
int main() { string s1("hello world"); s1.append("xxxxxx"); cout << s1 << endl; s1.assign("xxxxxx"); cout << s1 << endl; s1.insert(0, "hello"); cout << s1 << endl; s1.insert(5, "world"); cout << s1 << endl; s1.insert(0, 10, 'x'); cout << s1 << endl; s1.insert(s1.begin()+10, 10, 'y'); cout << s1 << endl; return 0; }
insert虽然看上去不错,但是效率比较低下,不宜多用。
5.erase
erase用于从pos位置删除n个字符
如下所示是这个函数的演示
6.replace
顾名思义,这个函数的意思是替代
如下所示是replace函数的一些使用
然而这个函数看似方便,实际上效率极低。一般不会轻易使用这个函数
比如说当我们想要将某个字符串的空格替换为20%的时候,我们可以使用这个函数,但是效率太低,不满意。我们可以使用其他的函数来实现这个功能
int main() { string s1("hello world hello world"); string s2; for (auto ch : s1) { if (ch != ' ') { s2 += ch; } else { s2 += "20%"; } } s1 = s2; cout << s1 << endl; }
7.c_str
这个函数的功能是返回他底层的字符串
这个函数一般是用于跟c的一些接口函数进行配合
int main() { string filename = "test.cpp"; FILE* fout = fopen(filename.c_str(), "r"); return 0; }
我们使用string类型而不是直接使用字符串的好处就在于,string的库非常丰富。我们可以直接去进行调用
8.find
顾名思义,他的功能就是查找,有如下几个函数重载
如果找到了对应的内容,就返回这个位置的下标
9.substr
find可以很方便的找到要找到内容的起始下标。而substr的功能则是取出字符类中字符串中的某一部分,并且返回这一部分
这样的话,我们就可以使用find和substr进行搭配,来进行网站的分割
我们知道网站分为三部分:协议,域名,资源
我们对一个网站进行分割的代码如下所示
int main() { string ur1 = "https://home.firefoxchina.cn/?fromwww"; size_t pos1 = ur1.find("://"); //协议 string protocol; if (pos1 != string::npos) { protocol = ur1.substr(0, pos1); } //域名和资源 string domain; string uri; size_t pos2 = ur1.find('/', pos1 + 3); if (pos2 != string::npos) { domain = ur1.substr(pos1 + 3, pos2 - (pos1 + 3)); uri = ur1.substr(pos2 + 1); } cout << protocol << endl; cout << domain << endl; cout << uri << endl; return 0; }
10.rfind
rfind和find类似,只不过他是从后往前找,这样的好处在于找一个文件的后缀,因为一个文件可能会有多个后缀,所以我们显然不可能从前往后找,我们必须从后往前找,故
11.find_first_of
从名字上来说,它的功能似乎与find类型?
其实不然,虽然它的参数也和find一样,但它的功能是从所给的字符串中,找到第一个和这里面中的任何一个字符相同的字符位置
如下代码所示,功能就是找出这句话中aeiou的位置,并将其替换为*字符
12.find_last_of
这个函数的功能和上面的是十分类似的,唯一不同的就是它是从后往前找的
13.relational operators
这里面其实就是一些运算符重载,其实里面的函数实现是存在一些冗余的,只需要实现对象和对象之间的比较即可,其他的都有隐式类型转换
14.operator+
这个运算符重载也很好理解,就是两个字符串进行相加,但需要注意的是,相加后是不改变原来的对象的
15.getline
这个函数的功能需要与cin对比来看,cin相当于scanf,getline相当于gets。cin当遇到空格时就不读了。而getline可以自己设置结束读取标志,或者默认换行时候才结束,这就在当我们读取一个句子的时候,由于有空格,我们就必须使用getline函数了
我们可以看下面这个例子
下面才是正确的代码
#include <iostream> using namespace std; int main() { string s; getline(cin,s); int len=s.size(); int pos=s.rfind(' '); if(pos!=string::npos) { cout<< len-(pos+1); } else { cout<< len; } }
16.string转为其他类型
在c语言中字符串转为其他类型时候有两个函数可以去调用:atoi,itoa
但是这两个函数其实并不是很好用
在c++中,我们有这样一系列的函数可以进行转换
17.其他类型转化为string类型
有以下函数重载可以实现这个功能
七、string与模板
我们也许会发现,string貌似跟类模板关系不是很大。
但其实string也是一个模板出来的
我们可以注意到,string其实typedef出来的
类似的,其实还有很多的string