C++--String类

news2024/10/6 14:35:02

系列文章目录


文章目录

目录

系列文章目录

文章目录

前言

一、为什么要学习string

1.c语言的字符串

2.OJ上的使用

二、string类的接口介绍

1.string简介

2.string构造成员函数

3.operator=函数

4.string容器size和length

5.重载operator[]和引用返回的意义

        5.1 operator[]

        5.2 at访问

        5.3.front 和 back

6.迭代器Iterators

范围for遍历

7.反向迭代器iterator

8.插入和修改

8.1 push_back尾插

8.2 append追加

8.3  +=常用​编辑

8.4 pop_back和erase

8.5 assign 分配

8.6 insert(重点)

8.7 erase  删除

8.8 replace 替换

8.9 swap交换

9.其他容器(resize,reserve····)

9.1 capacity、max_size

9.2 reserve、resize

1 reserve

2 resize

3 shrink_to_fit

10. 字符串操作

        10.1 c_str

        10.2 find

rfind的应用:

        10.3 find_first_of

        10.4 find_last_of

        10.5 find_not_first_of和find_not_last_of

        10.6 compare

11.非成员函数

     11.1 operator+

     11.2 relational operator

12.字符串和其他数组类型转换

三、字符串题目:

        1.仅仅反转字母

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

3.验证回文串

4.字符串相加

5.字符串最后一个单词的长度

6.getline

四、字符排序sort


前言

        STL(standard template libaray-标准模板库):是C++标准库的重要组成部分,不仅是一个可复用的组件库,而且是一个包罗数据结构与算法的软件框架。网上有句话说:“不懂STL,不要说你会C++”。STL是C++中的优秀作品,有了它的陪伴,许多底层的数据结构以及算法都不需要自己重新造轮子,站在前人的肩膀上,健步如飞的快速开发。


一、为什么要学习string

1.c语言的字符串

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

2.OJ上的使用

        在OJ中,有关字符串的题目基本以string类的形式出现,而且在常规工作中,为了简单、方便、快捷,基本都使用string类,很少有人去使用C库中的字符串操作函数。

二、string类的接口介绍

       在学习类时离不开接口的查询,所以我们通过文档来介绍string:
stringicon-default.png?t=N7T8https://legacy.cplusplus.com/reference/string/string/

1.string简介

        

string实际上是个类模板basic_string<template>. 这里涉及编码,我们后面在讲解。接下来我们开始介绍接口,将string理解为char类型的顺序表即可。

2.string构造成员函数

        构造函数:

我们介绍最常用的几个,分别是(1)(2)(4):

	string s1;                 //(1)
	string s2("hello string!");//(4)
	string s3(s2);             //(2)

我们使用cout和cin输出输入string实例化的对象

#include <iostream>
#include <string>
using namespace std;

void test_string01()
{
	string s1;
	string s2("hello string!");
	string s3(s2);
	
	cout << s1 << endl;
	cout << s2 << endl;
	cout << s3 << endl;

	cin >> s1;
	cout << s1 << endl;
}

int main()
{
	test_string01();

	return 0;
}

注意:string的输入和输出是被重载过的所以我们能使用。

对于这个我们可以猜想,pos时postion(位置)的缩写,是部分拷贝。关于npos我们也可以查找

无符号的整形-1,按照补码储存,整形最大值,由于一个数组开4G不现实,使用npos实际上就是结束位置。所以如果len > 字符长度或者不传时就是npos

void test_string01()
{
	string s1;
	string s2("hello string!");
	string s3(s2);

	string s4(s2, 6);
	cout << s4 << endl;

	string s5(s2, 6, 12);
	cout << s5 << endl;
}

结果:

下面这样也可以:

string s6("hello string!", 6, 12);

这样实际上是隐式类型转换,字符串"hello string!"构造临时对象,然后通过函数进行构造。

优化后是直接构造:

//构造
string s1("hello string!");
//隐式类型转换
string s2 = "hello string!";
//隐式类型转换有临时对象产生
const string& s3 = "hello string!";

C++类和对象(下)-CSDN博客icon-default.png?t=N7T8https://blog.csdn.net/Jk_Mr/article/details/138169401?spm=1001.2014.3001.5501上的文章中解释了隐式类型转换

对于(5)查看文档可知,(5)是拷贝字符串"···"前n个字符

对于(6),则是使用n个字符‘’进行构造:

后面几个了解一下。关于(7)涉及迭代器我们后面再将。

3.operator=函数

        赋值可以是字符串、字符或者string对象。

4.string容器size和length

string s1("hello string!\n");
cout << s1.size() << endl;
cout << s1.length() << endl;

sizelength是一样的。为什么呢?

实际上是因为string比STL出来的更早,当时定义长度的就是length。由于要保持接口一致性所以增加了size。

同样为了兼容c语言,字符串的最后一个实际上是\0.size和length都不计算\0.

5.重载operator[]和引用返回的意义

        5.1 operator[]

        

有了这个重载,string就可以像数组一样去使用。它的原理类似于:

class string
{
public:
	//引用返回
	//1、减少拷贝
	//2、修改返回值
	char& operator[](size_t i)
	{
		assert(i < _size);
		return _str[i];
	}
private:
	char* _str;
	size_t _size;
	size_t _capacity;
};

那么就可以像数组一样进行访问:

string s1("hello string!\n");
//可以像数组一样进行访问
for (int i = 0; i < s1.size(); i++)
{
	cout << s1[i] << ' ';
}

可以像数组一样进行修改:

//可以进行修改
for (int i = 0; i < s1.size(); i++)
{
	s1[i] = 'x';
}

对象开辟的空间在上。所以可以进行访问和修改。

并且string越界可以检测,使用的是断言assert

关于第二个const修饰的operator=()函数,是因为要访问不可修改的对象

下标[]实际上只有连续的空间能够支持,string、vector(顺序表)。

那么还有别的方式访问string 的内容吗?接下来我们介绍迭代器。

         5.2 at访问

和[]的功能是一样的,但是越界访问有区别。operator[]是断言没有商量的余地。

而at是抛异常,可以捕获。(异常后面讲解)

string s1("hello world!");
s1[50];

string s1("hello world!");
s1.at(50);

	string s1("hello world!");
	try {
		s1.at(50);
	}
	catch (const exception& e)
	{
		cout << e.what() << endl;
	}

        5.3.front 和 back

        front是获取首元素,back是尾元素。

6.迭代器Iterators

        迭代器通常包含指向容器中某个元素的指针或引用,以及一些操作符(如递增、递减等),用于在容器中移动和访问元素。

先看使用方法:
        可以将迭代器理解为指针或者像指针一样的东西(不一定是指针)

string s1("hello string!");
//遍历方式1:下标
for (int i = 0; i < s1.size(); i++)
{
	cout << s1[i] << ' ';
}
cout << endl;
//遍历方式2
string::iterator it1 = s1.begin();//iterator需要指定类域
while (it1 != s1.end())
{
	cout << *it1 << ' ';
	++it1;
}
cout << endl;

可以记住这个使用方式,使用typeid可以看变量的类型:

	cout << typeid(it1).name() << endl;

所以说不能认为it1是指针,只是用法像指针。

同样迭代器也可以修改内容:

迭代器是通用的,链表list、二叉树都有迭代器。可以观察list 的文档。

范围for遍历

//遍历方式3
for (auto s : s1)
{
	cout << s << ' ';
}

        元素赋值给s,自动往后,自动赋值。

        所有的容器都可以使用范围for,本质上它使用的就是迭代器

注意是赋值拷贝给s所以,不会改变s1的内容:

要改变则需要使用引用auto& s:

string也实现const迭代器:

即对象是一个const对象,使用非const迭代器可以修改,会报错,所以要使用const迭代器。

iterator是可读可写,const_iterator是只读。不是const  iterator,是要保护迭代器指向的内容,而不是保护迭代器本身

实际上是这里使用auto就不用考虑了,auto根据右边进行推导。

7.反向迭代器iterator

        反向迭代器就是从后往前迭代。

它的++是倒着走的。

string s1("hello string!");
auto it1 = s1.begin();
while (it1 != s1.end())
{
	cout << *it1 << ' ';
	*it1++;
}
cout << endl;

string::reverse_iterator it2 = s1.rbegin();
while (it2 != s1.rend())
{
	cout << *it2 << ' ';
	*it2++;
}
cout << endl;

由于string有[]重载,所以比较少用迭代器,但是迭代器是通用的。

这四个实际上可以当作const_iterator迭代器使用。

8.插入和修改

8.1 push_back尾插

        push_back()是插入一个字符:

string s;
s.push_back('a');
cout << s << endl;

8.2 append追加

        append可以尾插一串数据.所有的len是跨度的意思。

string s1("hello");
string s2(" string");
//追加string对象
s1.append(s2);
cout << s1 << endl;
//追加string对象的子串
s1 = "hello";
//从子串的0位置,跨度为2
s1.append(s2, 0, 2);
cout << s1 << endl;
//追加常量字符串的前n个
s1 = "hello";
s1.append(" strnig",4);
cout << s1 << endl;
//追加n个字符c
s1 = "hello";
s1.append(2,'s');
cout << s1 << endl;
//迭代器
s1 = "hello";
s1.append(s2.begin(),s2.end());
cout << s1 << endl;

  结果 :

8.3  +=常用

8.4 pop_back和erase

        pop_back()只支持删除一个。

        erase则支持删除一段:

8.5 assign 分配

        assign实际上和赋值差不多,和append差不多的解释:

8.6 insert(重点)

string s1("hello");
string s2("string ");
//pos位置string对象
s1.insert(0,s2);
cout << s1 << endl;
//pos位置string对象的子串
s1 = "hello";
s1.insert(0, s2, 0,3);
cout << s1 << endl;
//pos位置常量字符串的前n个
s1 = "hello";
s1.insert(0,"strnig ", 4);
cout << s1 << endl;
//pos位置n个字符c
s1 = "hello";
s1.insert(0,2, 's');
cout << s1 << endl;
//迭代器插入n个字符c
s1 = "hello";
s1.insert(s1.begin(), 2, 's');
cout << s1 << endl;
//迭代器
s1 = "hello";
s1.insert(s1.begin(), s2.begin(), s2.end());
cout << s1 << endl;

        可以用insert头插数据,但是insert进行插入,后面的数据都要后移,使用尽量少用,效率较低

8.7 erase  删除

        删出和insert一样要慎用,因为效率低,需要移动数据

string s1("hello world!");
cout << s1 << endl;

s1.erase(0, 5);
cout << s1 << endl;

s1 = "hello world!";
s1.erase(s1.begin());
cout << s1 << endl;

s1 = "hello world!";
s1.erase(s1.begin()+5, s1.end());
cout << s1 << endl;

        

insert和erase的len都可以传很长,因为有npos缺省,但是第一个位置参数不能超出实际范围。

8.8 replace 替换

        有了上面的基础,在观察replace的文档就不难了:

替换从字符位置pos开始并跨越len个字符(或字符串范围[i1,i2)之间的部分)的字符串部分为新内容:注意len是阔度,将长度为len的这一段替换成新的内容str。覆盖并插入。

string s1("hello world!");
string s2("and");
//从5位置开始,跨度为1,即将空格替换成and
s1.replace(5, 1, s2);
cout << s1 << endl;
//从5位置开始,跨度为1,即将空格替换成and
s1 = "hello world!";
s1.replace(s1.begin() + 5, s1.begin() + 6, s2);
cout << s1 << endl;
//将空格替换成%20
s1 = "hello world!";
s1.replace(5, 1, "%20");
cout << s1 << endl;

题目:
将字符s1中所有的空格替换成%20:

string s1("hello world! and hello string!");
for (size_t i = 0; i < s1.size();)
{
	if (s1[i] == ' ')
	{
		//替换//跨度为1
		s1.replace(i, 1, "%20");
		i += strlen("%20");
	}
	else
		i++;
}
cout << s1 << endl;

        其中也设计数据挪动效率较低。介绍一种更好的方法:

	string s1("hello world! and hello string!");
	string s2;//将数据放到另一个对象中
	for (auto s : s1)
	{
		if (s != ' ')//不是空格插入
			s2 += s;
		else
			s2 += "%20";//是空格就插入%20;
	}
	cout << s2 << endl;

牺牲空间换时间。

8.9 swap交换

        交换涉及深拷贝,后面会讲解,这里要会用。

交换成str。

	string s1("hello");
	string s2("string!");
	cout << s1 << endl;
	cout << s2 << endl;
	s1.swap(s2);
	cout << s1 << endl;
	cout << s2 << endl;

9.其他容器(resize,reserve····)

        9.1 capacity、max_size

	string s1("hello world!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	cout << s1.max_size() << endl;

        在vs2022(32位下)实际容量大小size大小不一样的.max_size就是string在32位下的最大。实际上也开不了max_size(),因为开一个字符串就到了2G左右,这么大的连续空间时开不出来的。

 观察下面代码,vs下是如何扩容的:

string s1;
size_t sz = s1.capacity();
cout << "capacity changed: " << sz << endl;
cout << "making s1 grow:\n";
for (int i = 0; i < 100; ++i)
{
	s1.push_back('c');
	if (sz != s1.capacity())
	{
		sz = s1.capacity();
		cout << "capacity changed:" << sz << endl;
	}
}

       结果:

        

        结果表明在vs下,实例化对象先开辟了16个空间,然后进行1.5倍扩容。

实际上当字符串小于16字节时,字符串存储在对象里面的buf数组:

当字符过长时则会在堆空间:

capacity指的是有效数据的空间,所以实际上都会多出一个给'\0'.

linux下的结果:

不同的平台扩容是不一样的。但是linux和vs下size和capacity都是指有效空间,实际空间多一个。

9.2 reserve、resize

       1 reserve

        resize意思是是改变size,实际上也会改变capacity。

        reserve则是影响capacity,不会影响size。

	string s1;
	cout << s1.capacity() << endl;
	s1.reserve(100);
	cout << s1.capacity() << endl;

说是给100,实际上vs会给多。

而linux则很老实是多少给多少:

接下来看看vs下reserve是否会缩容:

string s1;
cout << s1.capacity() << endl;
s1.reserve(100);
cout << s1.capacity() << endl;
s1.reserve(20);
cout << s1.capacity() << endl;

        

        默认不会缩容。

而如果当前有效数据个数小于16,并且缩容到大于有效个数小于16则capacity会变成15.将多余的空间释放掉.

如果有效数据个数大于等于16,则不会发生缩容。

而Linux很听话:

可以知道reserve在未知空间进行扩容可能不确定。

所以reserve使用场景是我们确定要靠多大,提前将空间开辟好:

后续就不需要扩容了。

 如果我们reserve超过有效数据的个数,然后访问我们所reserve的超过有效字节的空间是被允许的吗?

	string s1("hello string!");
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

	s1.reserve(100);
	cout << s1.capacity() << endl;

	cout << s1[50] << endl;

结果是不被允许的,因为operator[]重载时只允许访问有效空间:

所以缩容一般不用reserve,要使用reserve最好提前知道要开多少空间。

 2 resize

        如果n小于当前字符串长度,则当前值将缩短为其前n个字符,删除第n个字符之后的字符。

        如果n大于当前字符串长度,则当前内容将被扩展,插入到末尾的字符数将达到n。如果指定了c,则新元素将初始化为c的副本,否则它们将是值初始化的字符(空字符\0)。

vs下缩容:

	string s1("hello world! and hello vs! and hello string!");
	cout << s1 << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	s1.resize(12);
	cout << s1 << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

扩容:已有的不会动。

	string s1("hello world!");
	cout << s1 << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;
	s1.resize(20,'!');
	cout << s1 << endl;
	cout << s1.size() << endl;
	cout << s1.capacity() << endl;

注意resize缩容c++并未规定,不同平台缩容结果可能不同。所以缩容我们一般也不用resize。

3 shrink_to_fit

缩容一般用这个。

注意:空间追加和释放都不能分段追加和释放

10. 字符串操作

        10.1 c_str

        为了兼容c语言,对c语言的字符串操作进行使用,c++提供了c_str来获取字符串内容。(不可修改)

返回一个指针,指向一个包含以空字符结尾的字符序列的数组(即一个 C 字符串),该字符序列表示字符串对象的当前值。

        例如在读取文件时:

#define  _CRT_SECURE_NO_WARNINGS 1
#include <iostream>
#include <string>
using namespace std;

void test_string()
{
	string file("test.cpp");
	FILE* fout = fopen(file.c_str(), "r");
	char ch = fgetc(fout);
	while (ch != EOF)
	{
		cout << ch;
		ch = fgetc(fout);
	}
}


int main()
{
	test_string();
}

        10.2 find

        find是查找,从前往后找

与之相对的还有rfind是从后往前找

用法观察函数的参数就可以知道。

rfind的应用:

        获取file的后缀:

	string file("test.cpp");
	size_t pos = file.rfind('.');
	//string suffix(file.substr(pos))
	string suffix(file.substr(pos, file.size() - pos));
	cout << suffix << endl;

        substr是获取子串:

       取出域名:

    string url("https://blog.csdn.net/Jk_Mr"); 
	//找冒号:
	size_t pos1 = url.find(':');
	string url1 = url.substr(0, pos1 - 0);
	cout << url1 << endl;
	//找第三个斜杠
	size_t pos2 = url.find('/',pos1 + 3);
	string url2 = url.substr(pos1 + 3, pos2 - (pos1 + 3));
	cout << url2 << endl;
	//去到最后
	string url3 = url.substr(pos2 + 1);
	cout << url3 << endl;

        10.3 find_first_of

        字面上的意思是找到第一个字符,实际上它是找any匹配的字符;从前往后找。

// string::find_first_of
#include <iostream>       // std::cout
#include <string>         // std::string
#include <cstddef>        // std::size_t

int main ()
{
  std::string str ("Please, replace the vowels in this sentence by asterisks.");
  std::size_t found = str.find_first_of("aeiou");
  while (found!=std::string::npos)
  {
    str[found]='*';
    found=str.find_first_of("aeiou",found+1);
  }

  std::cout << str << '\n';

  return 0;
}

从代码结果可以看出,它是找到与参数任意一个匹配的位置。返回值是第一个匹配值的位置,即第一个e。这和c语言中的strtok函数类似。

        10.4 find_last_of

        从后往前找任何一个相匹配的字符,返回第一个相匹配的字符。

可用于匹配路径Linux和windows

void SplitFilename(const std::string& str)
{
	std::cout << "Splitting: " << str << '\n';
	std::size_t found = str.find_last_of("/\\");
	std::cout << " path: " << str.substr(0, found) << '\n';
	std::cout << " file: " << str.substr(found + 1) << '\n';
}

int main()
{
	std::string str1("/usr/bin/man");
	std::string str2("c:\\windows\\winhelp.exe");

	cout << str2 << endl;

	SplitFilename(str1);
	SplitFilename(str2);

	return 0;
}

        10.5 find_not_first_of和find_not_last_of

               跟find_first_of和find_last_of相反,是和参数都不匹配的

        10.6 compare

11.非成员函数

     11.1 operator+

        为什么要重载成全局函数?

	string s1("hello");
	string s2("world!");
	//这样可以重载成成员函数
	string s3 = s1 + s2;
	//但是为了支持第一个参数不是隐藏指针this
	//所以重载成成员函数
	string s4 = "hello " + s2;

为了支持第一个参数不是隐藏指针this,所以重载成成员函数。

        11.2 relational operator

        同样要支持第一个参数不是this指针,重载成全局函数

       是按照字典序比较的,从第一个开始,如果相同比较下一个的ASCII码值。不相同就返回。

注意:和流提取、流插入的优先级,需要加括号

实际上设计的有点冗余,因为const char* 可以隐式类型转换到const string&。

12.字符串和其他数组类型转换

        c语言中有atoi和itoa用于字符串转换整形整形转换字符串。C++也有类似的接口:

        to_string:

三、字符串题目:

        1.仅仅反转字母

917. 仅仅反转字母 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/reverse-only-letters/description/

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

  • 所有非英文字母保留在原有位置。
  • 所有英文字母(小写或大写)位置反转。

返回反转后的 s 。

忽略非字母即可,判断是否为字母,是字母就进行反转:

class Solution {
public:
    bool IsAlpha(char s)
    {
        if(s >='a' && s<= 'z')
        return true;
        if(s >= 'A' && s <= 'Z')
        return true;

        return false;
    }

    string reverseOnlyLetters(string s) {
      if(s.empty())
      return s;

      int begin = 0 , end = s.size()-1;
      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.字符串中的第一个唯一字符

387. 字符串中的第一个唯一字符 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/first-unique-character-in-a-string/description/使用计数排序思想:

class Solution {
public:
  int firstUniqChar(string s) 
  {
    //范围小可以使用计数排序,统计每个字母出现的次数
    //然后返回出现一次的字母
    int count[26] = {0};
    for(auto ch : s)
    {
        count[ch -'a']++;//和a的相对位置,a就是下标0位置
    }
    //s中的每一个字符已经映射到了数组count上,出现一次的被记录,或者没有会出现一次的
    //遍历数组查看每个字符的出现次数
    for(size_t i = 0 ; i < s.size(); i++)
    {
        if(count[s[i] - 'a'] == 1)//每个字符相对'a'的差值就是数组count映射的位置
        {
            return i;
        }
    }
    return -1;
  }
};

正反查找:

class Solution {
public:
  int firstUniqChar(string s) 
  {
    //从首字符开始查找,正查找和逆查找的位置相同则说明该字符只出现了一次
    //若不相同则说明有俩个或俩个以上的字符。
    for(int i=0; i<s.size(); ++i)
    {
      int index = s.find(s[i]);
      int reverse_index = s.rfind(s[i]);  
      if(index == reverse_index)
        return i;
    }
    return -1;
  }
};

3.验证回文串

125. 验证回文串 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/valid-palindrome/逆置比较:

class Solution {
public:
    //判断并转换大写为小写
    bool IsAlphaOrDigit(char& s)
    {
        if(s >= 'a' && s<= 'z')
        return true;
        if(s >= 'A' && s <= 'Z')
        {
            s += 32;
            return true;
        }
        if(s >= '0' && s<= '9')
        return true;
        return false;
    }
    bool isPalindrome(string s) {
        string str;//将s逆置放入str,去掉非字母数字字符,大写变小写
        int end = s.size() - 1;
        for(int i = end; i >= 0; i--)
        {
            if(IsAlphaOrDigit(s[i]))
            {
                str += s[i];//尾插
            }
        }
        string com(str);//逆置比较
        reverse(com.begin(),com.end());
        return com == str;
    }
};

//时间:O(N) 空间:O(N)

可以使用tolower来进行转换

双指针比较:

class Solution {
public:
    bool IsAlphaOrDigit(char& s)
    {
        if(s >= 'a' && s<= 'z')
        return true;
        if(s >= 'A' && s <= 'Z')
        {
            s += 32;//转换大小写
            return true;
        }
        if(s >= '0' && s<= '9')
        return true;
        return false;
    }

    bool isPalindrome(string s) {
        //双指针法进行判断
        int begin = 0, end = s.size() - 1;
        while(begin < end)
        {
            //左侧找到字母或数字//同时转换大小写
            while(begin < end && !IsAlphaOrDigit(s[begin]))
                begin++;
            //右侧找到字母或数字//同时转换大小写
            while(begin < end && !IsAlphaOrDigit(s[end]))
                end--;
            //将左右比较,不相同false
            if(s[begin] != s[end])
                return false;
            else
            {
                begin++;
                end--;
            }
        }
        return true;
    }
};
//时间复杂度: O(N) 空间复杂度:O(1)

4.字符串相加

415. 字符串相加 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/add-strings/

/*
*思路:
* 1. 先逆置数据
* 2. 数据对应为进行相加
* 3. 最后若有进位,还需尾插数据,最后逆置恢复数据
*/
class Solution
{
public:
	string addStrings(string num1, string num2)
	{
		//逆置解过进行操作
		reverse(num1.begin(), num1.end());
		reverse(num2.begin(), num2.end());
		//num1和num2的下标
		int index1 = 0, index2 = 0;
		int carry = 0;//进位标致
		int res = 0;//转换数字保存量
		string ret;
		//循环进行相加,先考虑俩个数字的长度相同
		while (index1 < num1.size() || index2 < num2.size() || carry != 0)//若还有进位需要进位
		{
            //当进位不为0时index1和index2会超过字符串范围
            //所以需要判断,然后向前补carry的值res = 0 + 0 + 1;
            //有了这一步那么也保证了长短不一:因为超过长度会补0
			int x = index1 < num1.size() ? num1[index1] - '0' : 0;
			int y = index2 < num2.size() ? num2[index2] - '0' : 0;
            //单个相加
			res = x + y + carry;
			if (res >= 10)
			{
				carry = 1;
				ret += res % 10 + '0';
			}
			else
			{
				carry = 0;
				ret += res + '0';
			}
			index1++;
			index2++;
		}
        //将结果在反过来
		reverse(ret.begin(), ret.end());
		return ret;
	}
};
class Solution
{
public:
	string addStrings(string num1, string num2)
	{
		//num1和num2的下标
		int index1 = num1.size()  - 1, index2 = num2.size()  - 1;
		int carry = 0;//进位标致
		int res = 0;//转换数字保存量
		string ret;
		//循环进行相加,先考虑俩个数字的长度相同
		while (index1 >= 0 || index2  >= 0 )//若还有进位需要进位
		{
            //当进位不为0时index1和index2会超过字符串范围
            //有了这一步那么也保证了长短不一:因为超过长度会补0
			int x = index1 >= 0 ? num1[index1--] - '0' : 0;
			int y = index2 >= 0 ? num2[index2--] - '0' : 0;
            //单个相加
			res = x + y + carry;
			//处理进位
            carry = res /10;
            res = res % 10;

            //头插//每次都要挪动数据,所以建议尾插然后逆置
            //ret.insert(0,1,res + '0');
            ret += res + '0';
		}
        //进位标志不为0
        if(carry != 0)
        {
            ret += res + '0';
        }
        reverse(ret.begin(),ret.end());
		return ret;
	}
};

5.字符串最后一个单词的长度

        字符串最后一个单词的长度icon-default.png?t=N7T8https://www.nowcoder.com/practice/8c949ea5f36f422594b306a2300315da?tpId=37&tqId=21224&ru=/exam/oj        cin输入:

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    cin >> s;
    int pos = s.rfind(" "), last = s.size() - 1;
    cout<< last - pos;
    return 0;
}

为通过,vs上也是5,这说明cin出了问题。通过vs调试可以看出

cin遇到空格和换行就会结束提取,scanf(除了%c遇到空格不停)也是一样。

c++有一个专门的函数解决:

6.getline

getline默认是遇到换行才停止。

#include <iostream>
#include <string>
using namespace std;

int main() {
    string s;
    getline(cin,s);
    int pos = s.rfind(" "), last = s.size() - 1;
    cout<< last - pos;
    return 0;
}

所以c++中要获取字符串包含空格时,使用getline。

在使用while(cin >> s)时,结束可以用ctrl + C或者 ctrl + Z + 换行。

四、字符排序sort

        如果要对一个字符串进行排序该怎么排,由于string中没有专门的排序,所以我们给出下面的排序头文件,在STL中有一个头文件包含算法:

icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/algorithm/

        在这个头文件中:

sort是用于排序的,它是一个模板:

它的范围规定为必须是左闭右开的:

last为无效的位置。迭代器就可以适配这个范围。

	string s1("hello string!");
	cout << s1 << endl;

	//s1 按照字典序排序
	sort(s1.begin(), s1.end());
	cout << s1 << endl;

部分排序:


如果你又所收获,可以留下你的点赞和关注谢谢你的观看!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1672224.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

嫦娥六号揭秘真相:阿波罗登月是真是假?一文终结所有疑问!

近期&#xff0c;嫦娥六号的成功发射如同璀璨的星辰&#xff0c;再次将人们的视线聚焦于浩瀚的宇宙&#xff0c;与此同时&#xff0c;网络上关于美国阿波罗登月是否造假的争议也如潮水般涌现。一些声音宣称&#xff0c;嫦娥六号的发射为揭示美国阿波罗登月任务的真实性提供了关…

Java面试八股之String类的常用方法有哪些

Java中String类的常用方法有哪些 获取字符串信息&#xff1a; length()&#xff1a;返回字符串的字符数。 isEmpty()&#xff1a;判断字符串是否为空&#xff08;即长度为0&#xff09;。 访问单个字符&#xff1a; charAt(int index)&#xff1a;返回指定索引处的字符。 …

InstantStyle —— 文本到图像生成中的风格保持新突破

在人工智能领域&#xff0c;文本到图像生成&#xff08;Text-to-Image Generation&#xff09;技术正迅速发展&#xff0c;其应用范围从娱乐到专业设计不断扩展。然而&#xff0c;风格一致性生成一直是该领域的一个技术难题。最近&#xff0c;InstantX团队提出了一种名为Instan…

GEVernova推出GEV新能源平台,引领新能源未来

近日&#xff0c;全球领先的能源设备制造和服务公司 GE Vernova 宣布推出 GEV 新能源平台&#xff0c;这是一个将金融、科技和产业深度融合的全新投资平台。GEV 新能源平台旨在为用户提供一站式可持续新能源投资解决方案&#xff0c;助力全球新能源转型和可持续发展。 新能源已…

vs-qt中无法加载qsqlite驱动,但是单独新建demo测试却又是正常的。。。

开发环境: Vs2015 + qt5.12 背景: 接手了一个项目,可以编译过去,也可以运行,, 但是登录一直失败,,但是数据库文件也是正常的。。。 最主要的是环境和同事的是一样的,,,但是他那边可以加载成功,我这边不可以。。 后来单独在vs中创建了一个demo,用来测试QSqlData…

如何高效管理微信?快速掌握捷径!

对于那些需要管理多个微信号的人来说&#xff0c;如何高效地管理这些账号成为了一个难题。今天&#xff0c;就给大家分享一个管理多个微信号的捷径——微信管理系统。 通过微信管理系统&#xff0c;你可以轻松实现高效管理多个微信号&#xff0c;一起来看看吧&#xff01; 首…

一文汇总对比英伟达、AMD、英特尔显卡GPU

‍‍&#x1f3e1;博客主页&#xff1a; virobotics(仪酷智能)&#xff1a;LabVIEW深度学习、人工智能博主 &#x1f4d1;上期文章&#xff1a;『【仪酷LabVIEW AI工具包案例】使用LabVIEW AI工具包YOLOv5结合Dobot机械臂实现智能垃圾分类』 &#x1f37b;本文由virobotics(仪酷…

C语言/数据结构——每日一题(环形链表)

一.前言 今天在力扣上刷到一道链表题——环形链表https://leetcode.cn/problems/linked-list-cycle 想着和大家们分享一下。让我们直接开始今天的分享吧。、 二.正文 1.1题目描述 1.2题目分析 这道题是想让我们做出分析&#xff0c;该链表是不是带环链表&#xff0c;如果是…

mac定时任务、自启动任务

https://quail.ink/mynotes/p/mac-startup-configuration-detailed-explanation <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.d…

DIFT:Emergent Correspondence from Image Diffusion # 论文阅读

URL https://arxiv.org/pdf/2306.03881 主页&#xff1a;https://diffusionfeatures.github.io/ 代码&#xff1a;https://github.com/Tsingularity/dift TD;DR 23 年 6月 cornell 大学的文章&#xff0c;任务是做图片的特征匹配&#xff08;关联&#xff09;&#xff0c;特…

Github入门10问,收藏~

Github是Python开发中最常用到的工具和资源&#xff0c;Github上Python相关的仓库多达300多万个&#xff0c;但有很多人还不知道怎么去使用Github&#xff0c;这里来通过10个问题来科普下。 什么是GitHub&#xff1f;为什么要学习使用GitHub&#xff1f;如何创建GitHub账户&…

VTK —— 三、标准格式 - 示例1 - 读取建模不同格式模型(支持.ply、.vtp、.obj、.stl、.vtk、.g等模型格式)(附完整源码)

代码效果 本代码编译运行均在如下链接文章生成的库执行成功&#xff0c;若无VTK库则请先参考如下链接编译vtk源码&#xff1a; VTK —— 一、Windows10下编译VTK源码&#xff0c;并用Vs2017代码测试&#xff08;附编译流程、附编译好的库、vtk测试源码&#xff09; 教程描述 本…

宝塔面板各种疑难杂症处理命令教程

下载地址&#xff1a;宝塔面板各种疑难杂症处理命令教程 这份宝塔面板各种疑难杂症处理命令教程&#xff0c;可以解决市面上遇到的各种难题&#xff0c;建议有技术能行的下载使用&#xff0c;小白也可以下载来学习可以帮助你解决宝塔面板遇到的各种难题

Java面试八股之什么是Java反射

什么是Java反射 基本概念 反射是Java语言的一个重要特性&#xff0c;它允许我们在运行时分析类、接口、字段、方法等组件的信息&#xff0c;并能够动态地操作这些组件&#xff0c;包括创建对象、调用方法、访问和修改字段值等。简单来说&#xff0c;反射提供了在程序运行时对…

镊子蜡烛如何抓住反转进行交易?昂首资本2步抓住反转

很多投资者通过之前的文章知道镊子烛台图&#xff0c;甚至可以通过镊子烛台图有多倍收益&#xff0c;但是很多投资者又迷惑了&#xff0c;为什么我没有通过镊子烛台图获得收益&#xff0c;甚至有时还会亏损收手。其实事情很容易理解&#xff0c;Anzo Capital昂首资本认为那是因…

MES管理系统在柔性制造中有何重要作用

在当今这个瞬息万变的商业环境中&#xff0c;制造业正经历着一场前所未有的转型。消费者需求的多样化和市场动态的快速变化要求企业必须具备高度的灵活性和适应性。为了应对这些挑战&#xff0c;柔性制造策略应运而生&#xff0c;它以其快速响应和灵活调整的能力&#xff0c;成…

WWW服务器搭建(2)——Apache服务器配置与管理

一、Apache简介 1.1 关于Apache Apache HTTP Server&#xff08;简称Apache&#xff09;是Apache软件基金会的一个开放源码的Web服务器&#xff0c;可以在大多数计算机操作系统中运行&#xff0c;由于其跨平台和安全性被广泛使用&#xff0c;是最流行的Web服务器端软件之一。…

2024 年中国大学生程序设计竞赛全国邀请赛(郑州)暨第六届CCPC河南省大学生程序 设计竞赛Problem L. Toxel 与 PCPC II

//sort bug下标 遍历dp. //没修负的bug肯定连续 #include<bits/stdc.h> using namespace std; #define int long long const int n1e611; int a,b,c[n],dp[n]; signed main() {ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>a>>b;for(int i1;…

彻底搞定找不到msvcp100.dll,无法继续执行代码的问题

当您在使用电脑过程中遇到程序运行异常&#xff0c;提示“缺失msvcp100.dll文件”时&#xff0c;不必过于焦虑&#xff0c;此问题可通过一系列简单步骤得到有效解决。MSVCP100.dll是Microsoft Visual C库的一部分&#xff0c;主要用于支持某些应用程序运行所需的特定功能。如果…

C++ 派生类的引入与特性

一 继承与派生 从上面的例子可以看出&#xff1a; 继承&#xff1a;一旦指定了某种事物父代的本质特征&#xff0c;那么它的子代将会自动具有哪些性质。这就是一种朴素的可重用的概念。 派生&#xff1a;而且子代可以拥有父代没有的特性&#xff0c;这是可扩充的概念。 1 C 的…