string类

一、string类的基本介绍

1.1  c语言中的字符串

C语言中,字符串是以'\0'结尾的一些字符的集合,为了操作方便,C标准库中提供了一些str系列
的库函数,但是这些库函数与字符串是分离开的,不太符合OOP的思想,而且底层空间需要用户
自己管理,稍不留神可能还会越界访问

这是c语言中使用的字符串,实际上c语言没有字符串的概念,它是一个字符数组

1.2  c++string类的介绍

先去看官方文档的介绍吧!

它本质上是由 typedef而来 ,是由类模版basic_string<char>实例化而来,用来记录一些字符。     

 既然是一个类,那么它就有成员变量、成员函数,操作符重载和六大默认构造                                 接下来,将要从这几个角度思考问题。

当然,在使用string类时,记得加上头文件<string>

二、string的默认函数

2.1 构造函数(constructor)

我们先看文档,从文档中查找函数

光看文档感觉好吓人呀!没事,先只看常用的,其他的不会以后再查表,这里会介绍最常用的,并且对其简化

一个一个来:

这个就是普通的默认构造函数,直接调用即可

string(): 构造空的string类对象,即空字符串

这里就是最简单的拷贝构造,参数就是一个const string类

string(const string&s)

 这是C语言的构造方式,为了兼容C语言

string(const char* s)

 

 string(const char* s,size_t  n)

 

size_t n这个参数就是来限定,字符个数的。 

string (const basic_string& str, size_type pos, size_type len = npos);                                                 

这个函数,也是拷贝构造,是个重载,pos表示从下面为pos的值开始读取

len表示读取的长度,后面的缺省值(npos)很重要

我们转到它的定义去看

简单分析 size_t是无符号的数,-1的补码为  11111111111111111111111111111111,所以对于无符号数来说他的值为INT_MAX就是int类型最大值。

那么该函数,表示的就是,从pos位置copy这个字符串 n个字符。若没有n这个值,那么就会从pos位置copy所有值。

这里传的是引用啊!不是常量,a2 a3 a4 一个是从下标为6的位置分别度 1、2,npos个。              要注意pos指的是下标位置,比如说,第一个字符的pos值是0,pos为6时,此时为第七个字符                       

string(size_t n, char c)  string类对象中包含n个字符c

 2.2 析构函数

直接查表:

可以注意的是:表中没有关于析构的实现,所以,我们不用注意这一点,毕竟在销毁时,它会自动调用,所以不必在意

2.3 赋值运算符重载

先看看文档

这么三种函数,一个是赋值引用,一个是C语言的字符串(字符串的首地址),一个是字符,可以举例看看

 值的注意的是,赋值运算符是对已经存在的对象进行复制,如果:

string a1=a2;//这是是拷贝构造,而不是赋值

三、string的操作符重载

3.1 流插入符与流输出符

<< >>使用这两个操作符,可以对string进行io流操作这个很简单

3.2 判断大小类操作符

在文档中就是这么多,哈哈!!!

大家认真观察就可以知道,无非就是三种:

1. 引用(自己的变量)  与 引用相比

2. 常量  与 引用 相比

3. 引用 与 常量 相比

在此之前,还有介绍字符串相比的规则

1. 相等与不相等:两个字符串完全一样,字符数量,与字符一 一相等,不相等就是它的反向逻辑

“abcdef”  与 "abcdef"相等    “aaaaaa”与“bbbbbb”不相等

2.大于 :第一个字符串大于第二个字符串:两个字符串中,第一个不相等的字符,左边的字符大于右边字符的Ascll值,还一种情况是,左右两边的字符串Ascll值一直相等,但是,左边字符串更长

"abcdef"大于"abcdea"  f的Ascll值大于a

“ abcde”大于" abcd"  左边字符串比右边长

其实介绍完这两个,其他的就不用介绍了

3. 小于:不就是 大于等于取反吗?

4.大于等于:不就是大于和等于或一下

5.小于等于:就是大于取反

这样就里顺了逻辑了。

不过还有一个问题,为什么没有常量与常量相比呢?认真想一想,常量与常量相比,还需要调用函数吗?我们自己都能看出来,对吧!

这里就借用文档的例子吧!

#include <iostream>
#include <vector>

int main ()
{
  std::string foo = "alpha";
  std::string bar = "beta";

  if (foo==bar) std::cout << "foo and bar are equal\n";
  if (foo!=bar) std::cout << "foo and bar are not equal\n";
  if (foo< bar) std::cout << "foo is less than bar\n";
  if (foo> bar) std::cout << "foo is greater than bar\n";
  if (foo<=bar) std::cout << "foo is less than or equal to bar\n";
  if (foo>=bar) std::cout << "foo is greater than or equal to bar\n";

  return 0;
}

这个好理解。

3.3 +=运算符

这个很简单,但是用的是非常多的,而且效率更高,对于尾接一个字符串

直接举例:

 3.4 []运算符

它有两种,一种是常量另一种是普通变量

出现[]重载的原因是:要兼容c语言,可以把当成字符数组来看待,毕竟它的底层其实就是一个动态字符数组。

当然,这里也会有使用时越界问题:

 对于c++而言,它会自己报错,所以越界了不用担心该就是了!

四、string的容量成员函数 

4.0 容量与长度的介绍

string的底层是一个动态数组,那么这个动态数组里头,有size成员和capacity成员

当size==capacity时,就会进行扩容,一般的编译器是1.5倍扩容,所以一般来说容量都会是对齐。当然这里的容量不包含‘\0’,实际容量大1.所以容量与长度不是同一个概念,容量比长度大。

4.1 length和size函数       

   

从底层实现来看,他们的效率都是一样,只是名字不同而已,length是为了兼容c语言而设计的,他们的功能:返回字符串的长度,注意就是不包括  '\0' 实际的长度要多一个。

这里STL库为了统一,一般使用size

4.2 max_size 

这个函数是返回字符串的最大长度

由于已知的系统或库实现限制,这是字符串可以达到的最大潜在长度,但不能保证对象能够达到该长度:在达到该长度之前,它仍然可能在任何时候无法分配存储。

这个返回值,是不确定的!因为系统是不同的

我们还是举例子:

在X86的情况下:开出的最长字符串时2的31次方减1

这是计算器算的数

这是在X64的环境:开出的最长字符串时2的61次方减1

这是计算器算出的数,一模一样

4.3 capacity

返回字符串的容量,没有什么可以讲的。

4.4 resize

调整字符串容量

如果n是小于字符串的长度,那么size会缩小到n个字符,但是容量是不变的

如果n是大于字符串的长度,如果指定了字符,那么就会使用这个指定的字符,去扩展整个字符串,直到容量大于等于n,如果没有指定的话就会使用'\0'来扩展,所以说resize的作用,在于缩小字符串size与扩大字符串的容量,他只会扩大容量不会缩小容量,它是会改变字符串的内容的,慎用!!

举例:

int main()
{
	string a("hello"); 
	cout << a << endl;
	cout <<"容量为"<< a.capacity() << endl;
	a.resize(1);
	cout << a << endl;
	cout <<"容量为"<< a.capacity() << endl;
	a.resize(10);
	cout << a << endl;
	cout << "容量为"<< a.capacity() << endl;
	a.resize(40, 'a');
	cout << a << endl;
	cout << "容量为" << a.capacity() << endl;
	a.resize(1);
	cout << a << endl;
	cout << "容量为" << a.capacity() << endl;
	return 0;
}

这里就验证了,我们的说法了,resize最好是用于需要改变字符串内容的场景

4.5 reserve

 它的功能更像是弥补resize的不足,OK

Requests that the string capacity be adapted to a planned change in size to a length of up to n characters.

要求字符串容量适应计划中的大小变化,长度最多为n个字符

就是这里可以调整容量到n,如需扩容就会自动扩容,但是对于缩容是由编译器定义的,有些会缩容,有些不会。

In all other cases, it is taken as a non-binding (不具约束力)request to shrink the string capacity: the container implementation is free to optimize otherwise and leave the string with a capacity greater than n.

也就是缩容是不确定的!


This function has no effect on the string length and cannot alter its content.           

无法改变字符串的内容!!

认真观察,其实它的容量是对齐的,而且在vs里它不会缩容,只会扩容,但是它的扩容效率比一般扩容更高它是一次性全部扩完,而一般的扩容是一次一次扩容的。不过差不了多少,1.5倍扩 容也很快。

但是啊!在Linux环境下,它是会缩容的!会缩到刚好容纳的下size。

4.6 clear以及empty

学过数据结构的一般都熟悉这两个接口:

clear()只是将string中有效字符清空,不改变底层空间大小

empty判空,判断string是否为空

这个就不举例子了,太简单了

4.7 简单介绍string类的扩容底层

string类一般有四个成员函数,

char buff[16];

char *a;

int capacity;

int size;

看看正好对应了这个类的大小

对于后三个成员,他们的作用就是扩容,存字符,当然buff数组的作用是当字符串的长度没有超过15时,此时使用buff数组来记录字符串,当超过15时,编译器就使用动态开辟堆里的空间存储字符

不同编译器下实现也就不同,但是大体的逻辑是不变的!

五、string的遍历方法

5.1  迭代器

迭代器类似于指针,指针只是指向一个变量,但是迭代器是指向一个自定义类型,他们都是用来访问内容的,下面就可以使用迭代器去遍历string类

直接看文档,一共八个迭代器

没关系一个一个来

先看begin和end

他们是一个获取首位置,以获取尾位置,对于end来说它获取的位置为有效位的下一个

end: Returns an iterator pointing to the past-the-end character of the string.

看代码如何遍历:

int main()
{
	string a("hello world");
	string::iterator it = a.begin();
	for (; it != a.end(); ++it)
	{
		cout << *it <<" " ;
	}
	return 0;
}

再看  rbegin() 以及  rend()

rbegin()是返回最后一个有效位置,而rend是返回第一个有效位置之前的位置。注意他们是反向迭代器,所以移动的方向也会反向。

那么看代码吧!!

int main()
{
	string a("hello world");
	string::reverse_iterator it = a.rbegin();
	for (; it != a.rend(); ++it)
	{
		cout << *it <<" " ;
	}
	return 0;
}

再看 cbegin() cend(),他们是常量迭代器,不能修改字符串的内容。当然前面的非常量迭代器是可以修改string的。我们这里长话短说

string ::const_iterator it=a.cbegin();

同理crend() 以及  crbegin()的道理是一样的,他们只是反向的常量迭代器

string ::const_reverse_iterator it=a.crbegin();

5.2 auto和范围for

auto关键字

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,后来这个不重要了。C++11中,标准委员会变废为宝赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。


用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&
当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。


auto不能作为函数的参数,可以做返回值,但是建议谨慎使用


auto不能直接用来声明数组

auto也不能只定义不初始化

易错案例1: 

#include<iostream>
using namespace std;
int func1()
{
return 10;
}
// 不能做参数
void func2(auto a)
{}
// 可以做返回值,但是建议谨慎使用
auto func3()
{
return 3;
}

易错案例2:

int a = 10;
auto b = a;
auto c = 'a';
auto d = func1();
// 编译报错:rror C3531: “e”: 类型包含“auto”的符号必须具有初始值设定项
auto e;
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;

易错案例3:

int x = 10;
auto y = &x;
auto* z = &x;
auto& m = x;
//error C3535 : 无法推导“auto * ”的类型(依据“int”)
auto*n=x;

 易错案例4:

auto aa = 1, bb = 2;
// 编译报错:error C3538: 在声明符列表中,“auto”必须始终推导为同一类型
auto cc = 3, dd = 4.0;
// 编译报错:error C3318: “auto []”: 数组不能具有其中包含“auto”的元素类型
auto array[] = { 4, 5, 6 };

这样就把易错点归纳完了。

接下来看范围for

对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围,自动迭代,自动取数据,自动判断结束。


范围for可以作用到数组和容器对象上进行遍历


范围for的底层很简单,容器遍历实际就是替换为迭代器,这个从汇编层也可以看到。           

int main()
{
	string arr = "abcdefg";
	//c++98的遍历
	for (int i = 0; i < arr.size(); ++i)
	{
		arr[i]++;
	}
	for (int i = 0; i < arr.size(); ++i)
	{
		cout<<arr[i];
	}
	cout << endl;
	//c++11 范围for遍历
	for (auto& ele : arr)
	{
		ele += 1;
	}
	for (auto ele : arr)
	{
		cout << ele;
	}
	return 0;
}

注意点:

没有取引用的范围for是一个临时拷贝,不改变string的内容,但是加上了&就可以去改变string

auto 是自动识别类型会再编译是自动转化为对应的类型,但是使用多了容易造成可读性差

使用[]采用c语言也可以遍历字符串

六、string类修改对象成员函数

6.1 append()

作用:在字符串后面追加一个字符串

int main()
{
	string a("haha");
	a.append("haha");
	cout << a << endl;//追加一个常量字符串
	string b("hello world");
	a.append(b, 6, 5);
	cout << a << endl;//从下标为6的位置开始追加5个字符
	string c;
	c.append(b, 0);
	cout << c << endl;//从下标为0的位置开始追加,npos(很大的数)个字符
	string d;
	d.append(b);
	cout << d<< endl;//追加一个字符串
	string e;
	e.append("hahahaha", 4);
	cout << e<< endl;//追加一个常量字符串的前4个
	string f;
	f.append(5, 'a');
	cout << f << endl;//追加5个'a'字符
	return 0;
}

这里其实和构造是很像的。不过+=也可以完成这个操作.设计了很多了接口,但是用的多就是参数为拷贝构造的那个

6.2  push_back()尾插

这个就很简单了,但是是用的很多的一个函数

就是在尾部插入一个字符即可。

6.3 insert pos位置插入

还是看代码吧!

int main()
{
	string a("*****");
	string b("aaaaaa");
	a.insert(0,"haha");//在0位置插入一个常量字符串
	a.insert(3, a);//在3位置插入字符串
	a.insert(2,"a");//在2位置插入字符串 "a"
    char ch='a';
	a.insert(a.begin(), 'c');//头插一个字符
	a.insert(0, 1, ch);//头插一个字符
	return 0;
}

 其实这里设计的很乱,确实很乱!

我们标记几个重点:

用的最多的还是 a.insert(0,b)和a.insert(begin(),'a');

这里的insert写的是头插

 在使用insert时,一定得注意,insert的使用时间复杂度较高为n,使用时谨慎

6.4  erase pos位置删除

Erases part of the string, reducing its length:

int main()
{
	string a("hello world");
	//头删
	a.erase(0, 1);
	cout << a << endl;
	a.erase(a.begin());
	cout << a << endl;
	//尾删
	a.erase(a.size() - 1, 1);
	cout << a << endl;
	a.erase(--a.end());
	cout << a << endl;
	//删除pos位置后的所有字符
	string s("hello world");
	s.erase(5);
	cout << s << endl;
    //把所有字符全部删除完
    string b("hahaha");
    b.erase(b.begin(), b.end());
    cout << b << endl;
	return 0;
}

6.5 replace 替代函数

Replaces the portion of the string that begins at character pos and spans len characters (or the part of the string in the range between [i1,i2)) by new contents:

将字符串中从字符pos开始并跨越len字符的部分(或字符串中[i1,i2之间的部分)替换为新内容:

这个函数就是两个区间的替换

更多例子就不举例了,前面已经举例很多例子了,可以举一反三了!!!

6.6 swap

这个函数非常简单,就是把两个类的成员函数进行交换,以达到内容交换

6.7 pop_back()

 这个函数同样简单,就是对string进行尾删。

6.8 find系列函数

find()

Searches the string for the first occurrence of the sequence specified by its arguments.

在字符串中搜索其参数指定的序列的第一个匹配项。

匹配项可以是字符串,也可以是字符

这样就完成了匹配,注意如果没有找到就返回无符号数-1,也就是npos,所有的find类的返回值都是这样,找到了返回下标,没有找到就返回无符号数-1.

rfind()

它的参数类型与find一模一样,只是find是从左边寻找,而rfind()是从右边开始寻找的,既可以找一个字符又可以找一个字符串。

find_first_of

 什么意思呢?

它这个名字取得不太好,意思是:从pos位置,(从左往右,左闭右开),开始找,所有存在于

另一个字符串或者是字符的内容,找到第一个就返回其位置,没有找到就返回-1

看例子:

这里咱们就把所有有关"ld"的部分全部转化为'*',这是很有意思的,所实话,咱们的敏感字屏蔽就是这么来的,可以举例子让大家开心一下

find_last_of

这个函数与find_first_of几乎是一致的,就是查找方向是反的,怎么说,它是从后往前查找的

我们用这个来举例子:来看看英语的敏感字屏蔽。

这样,就可以做到屏蔽脏话

find_first_not_of和find_last_not_of

也同样道理,加上not就是不找指定的字符串呗,这个也好说

6.9  substr

它的功能就是返回一个从pos位置开始len长度的子串,len不写就是就是从pos位置开始的最长子串,这个我们用的多

 

 这个很简单,也好用,就是复杂度为O(n);

七、 string类的小练习

7.1 第一例:

在一个字符串中把所有的空格替换成"%%"

这个思路还是很简单:直接find()查找空格,然后使用replace()替换即可

改进思路:牺牲空间以换时间

int main()
{
	string a("hello cogou hello haha");
	string b;
	int i = 0;
	while (a[i] != '\0')
	{
		if (a[i] == ' ')
		{
			b += "%%"; i++;
		}
		else
			b += a[i++];
	}
	cout << b << endl;
	return 0;
}

7.2 第二例:

c_str的应用

这个作为补充知识的,c_str是为了兼容C语言而创建的,为了以后方便调用数据库,它是返回字符串的首地址的,如果没有这个函数,其实对于string而言是没有这个概念的,当然从底层来讲,他当然是一个从堆里开空间的动态数组

举一个文件操作吧!

int main()
{
	string a;
	cin >> a;
	FILE* fout = fopen(a.c_str(), "r");
	char ch = fgetc(fout);
	while (ch != EOF)
	{
		cout << ch;
		ch = fgetc(fout);
	}
	fclose(fout);
	return 0;
}

以后需要连接数据库时就要用到它。

7.3 第三例:

给一个完整的文件路径的字符串,要求文件与路径分离

 补充:linux的文件分隔符为/ 而windows的文件路径分隔符为\

思路不难,使用find_last_of 查找最后一个分隔符,在使用substr获取子串即可

void fun(const string& x)
{
	size_t pos = x.find_last_of("\\/");
	string a = x.substr(0, pos);
	string b = x.substr(pos + 1);
	cout << a << endl;
	cout << b << endl;
}
int main()
{
	string a("C:\\Users\\Default\\AppData\\Roaming\\Microsoft\\Spelling");
	string b("C:/Users/Default/AppData/Roaming/Microsoft/Spelling");
	fun(a);
	fun(b);
	return 0;
}

7.4 第四例:

getline的补充知识

delim是用于读取字符串时,修改的结束字符

cin在读取时是不会读取空格的,但是如果一个字符串了就是有空格,而且需要读取输入,此时可以使用getline来解决问题。

 字符串最后一个单词的长度_牛客题霸_牛客网 (nowcoder.com)

看这个题目吧!

计算字符串最后一个单词的长度,单词以空格隔开,字符串长度小于5000。(注:字符串末尾不以空格为结尾)

输入描述:

输入一行,代表要计算的字符串,非空,长度小于5000。

输出描述:

输出一个整数,表示输入字符串最后一个单词的长度

示例1

输入:

hello nowcoder

输出:8

说明:最后一个单词为nowcoder,长度为8

#include <iostream>
using namespace std;
int main() {
    string a;
    getline(cin,a,'\n');
    size_t pos=a.rfind(' ');
    cout<<a.size()-1-pos;
    return 0;
}

7.5 第五例:

917. 仅仅反转字母 - 力扣(LeetCode)

给你一个字符串 s ,根据下述规则反转字符串:

所有非英文字母保留在原有位置。

所有英文字母(小写或大写)位置反转。

返回反转后的 s

经典双指针问题:

class Solution {
public:
    inline bool panduan(char a)
    {
        return (a>='a'&&a<='z')||(a>='A'&&a<='Z');
    }
    string reverseOnlyLetters(string s) {
        int left=0;
        int right=s.size()-1;
        while(left<right)
        {
            while(left<right&&!panduan(s[left]))
            {
                left++;
            }
            while(left<right&&!panduan(s[right]))
            {
                right--;
            }
            swap(s[left++],s[right--]);
        }
        return s;
    }
};

7.6 第六例:

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)

这个题目在过去的哈希表题目讲解中写过,但是又出现了

直接上代码

class Solution {
public:
    int firstUniqChar(string s) {
    unordered_map<char,int>mp;
    for(char ch:s)
    {
        mp[ch]++;
    }
    for(int i=0;i<s.size();i++)
    {
        if(mp[s[i]]==1)
        return i;
    }
    return -1;
    }
};

7.7 第七例:

125. 验证回文串 - 力扣(LeetCode)

如果在将所有大写字符转换为小写字符、并移除所有非字母数字字符之后,短语正着读和反着读都一样。则可以认为该短语是一个 回文串 。

字母和数字都属于字母数字字符。

给你一个字符串 s,如果它是 回文串 ,返回 true ;否则,返回 false 

分析:

还是一个简单的题目,可以先把它转化,在使用双指针

class Solution {
public:
    inline bool panduan(char x)
    {
        return (x>='a'&&x<='z')||(x>='A'&&x<='Z')||(x>='0'&&x<='9');
    } 
    bool isPalindrome(string s) {
        string a;
        for(auto &ele:s)
        {
            if(panduan(ele))
            {
                if((ele>='A'&&ele<='Z'))
                {
                    a+=ele+32;
                }
                else
                a+=ele;
            }
        }
        int left=0;int right=a.size()-1;
        while(left<=right)
        {
            if(a[left++]!=a[right--])
            return false;
        }
        return true;
    }
};

7.8 第八例:

415. 字符串相加 - 力扣(LeetCode)

给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。

你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

思路:采用相加的运算法则,倒过来计算字符串,最后逆置

class Solution {
public:
    string addStrings(string num1, string num2) {
        string sum;
        int addr=0;
        int i=num1.size()-1;int j=num2.size()-1;
        while(i>=0||j>=0)
        {
            int num=0;
            if(i>=0)
            {
                num+=num1[i]-'0';--i;
            }
            if(j>=0)
            {
                num+=num2[j]-'0';--j;
            }
            num+=addr;
            addr=num/10;
            num%=10;
            sum+=num+'0';
        }
        if(addr)
        sum+='1';
        reverse(sum.begin(),sum.end());
        return sum;
    }
};

这里补充知识:

1.reverse它是一个全局的函数,逆置容器的底层是迭代器

只要把迭代器的范围标注,就会逆置范围内的内容。

2.字符转化为数字与数字转化为字符都可以查文档

下面是字符变各种类型的数字

 <string> - C++ Reference (cplusplus.com)

这里是数字变成字符

7.9 第九例:

 541. 反转字符串 II - 力扣(LeetCode)

给定一个字符串 s 和一个整数 k,从字符串开头算起,每计数至 2k 个字符,就反转这 2k 字符中的前 k 个字符。

如果剩余字符少于 k 个,则将剩余字符全部反转。

如果剩余字符小于 2k 但大于或等于 k 个,则反转前 k 个字符,其余字符保持原样。

541. 反转字符串 II - 力扣(LeetCode)

这个思路比较简单:就是使用reverse函数,注意区间问题即可

class Solution {
public:
    string reverseStr(string s, int k) {
        int i=0;//开始位置
        int j=s.size()-1;//终止位置
        while(1)
        {
            //运行位置
            if(i+2*k-1<=j)
            {
                reverse(s.begin()+i,s.begin()+i+k);
                i+=2*k;
            }
            else if(i+k-1<=j)
            {
                reverse(s.begin()+i,s.begin()+i+k);
                break;
            }
            else
            {
                if(i+k-1>j&&i<=j)
                reverse(s.begin()+i,s.end());
                break;
            }
        }
        return s;
    }
};

7.10 第十例:

557. 反转字符串中的单词 III - 力扣(LeetCode)

给定一个字符串 s ,你需要反转字符串中每个单词的字符顺序,同时仍保留空格和单词的初始顺序。

 

思路:还是如此find()找到空格,逆置单词

class Solution {
public:
    string reverseWords(string s) {
        int  pos=s.find(' ',0);
        int init=0;
        while(pos!=string::npos)
        {
            reverse(s.begin()+init,s.begin()+pos);
            init=pos+1;
            pos=s.find(' ',init);
        }
        if(s[init]!='\0')
        reverse(s.begin()+init,s.end());
        return s;
    }
};

  • 30
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值