文章目录
0. 前言
最近在牛客网上做题的时候,遇到了很恶心的输入数据,平常我们刷题,输入数据一般都是以下形式
- 第一行输入一个正整数 n,代表数组的大小
- 第二行输入 n 个正整数 a[i],代表给定的数组
但我遇到的那道题是输入格式是这样的 [1, 2, 3, 4, 5]
(数字之间用英文的逗号和空格分割),不告诉你有多少个数,整体就是一个字符串,让你自己处理
由于很少接触这样的输入格式,而且 C++ 的 string 类没有提供分割字符串的函数,在处理输入数据上花费了一定的时间(要是用 Java 或 Python 写,调用封装好的方法就行了,奈何自己刷题用的是 C++ )
为了下次遇到这样的输入数据时能够减少处理输入数据所耗费的时间,现在做一个总结
处理这样的数据主要有三步:
- 读取整个字符串
- 将字符串分割,得到一个字符串数组
- 将字符串数组转换为数字数组
1. 读取整个字符串
在用传统的 scanf、cin 等方式读取数据时,遇到空格时会停止读取,为了能够读取整个字符串,我们需要使用 getline 函数
string str;
getline(cin, str);
2. 将字符串分割,得到一个字符串数组
由于 C++ 的 string 类没有提供分割字符串的函数,需要我们手写一个分割字符串的方法(对 string 类不太熟悉的同学,可以看下我的另一篇博文:C++string类的常用方法)
分割字符串主要利用 string 类提供的 find 方法,为了提高函数的重用性,函数可以接收三种类型的参数(函数重载):
- string separator
- char separator
- char separator[]
/**
* 使用指定的分隔符将字符串拆分为字符串向量
*
* @param separator 分隔字符串
* @param source 被拆分的源字符串
* @return 返回一个包含拆分后字符串的向量
*
* 说明:
* 1. 如果源字符串为空,则直接返回空向量
* 2. 拆分过程从头开始,找到每次出现分隔符的位置,然后截取该位置前的子串添加到结果向量中
* 3. 如果找不到分隔符,则将整个源字符串添加到结果向量中,并结束循环
* 4. 每次找到分隔符后,更新起始位置为分隔符结束位置之后,以继续下一次拆分
* 5. 循环结束时,返回包含所有拆分字符串的向量
*/
vector<string> split(string separator, string source) {
vector<string> result;
if (source.length() == 0) {
return result;
}
int start = 0;
while (true) {
int position = source.find(separator, start);
if (-1 == position) {
position = source.length();
}
string temp = source.substr(start, position - start);
result.push_back(temp);
if (position == source.length()) {
break;
}
start = position + separator.length();
}
return result;
}
vector<string> split(char separator, string source) {
string str;
str += separator;
return split(str, source);
}
vector<string> split(char separator[], string source) {
string str = separator;
return split(str, source);
}
3. 将字符串数组转换为数字数组
在将字符串数组转换为数字数组之前,我们先来了解一下字符串转数字的方法
3.1 字符串转数字
字符串转数字主要有两种方法:
- 自己手写一个字符串转函数的函数
- 利用 string 头文件提供的 stoi 方法(string to integer)
为了提高函数的重用性,函数可以接收两种类型的参数(函数重载):
- string source
- char source[]
3.1.1 自己手写一个字符串转数字的函数
如果要自己手写一个字符串转数字的函数,只需要借助基础的运算符号
int stringToInt(string source) {
int result = 0;
for (int i = 0; i < source.length(); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
int stringToInt(char source[]) {
int result = 0;
for (int i = 0; i < strlen(source); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
3.1.2 利用 string 头文件提供的 stoi 方法(string to integer)
注意:
stoi
函数是在 C++11 标准中引入的,它是<string>
头文件中提供的一个标准库函数,用于将字符串转换为整数
int stringToInt(string source) {
return stoi(source);
}
int stringToInt(char source[]) {
return stoi(source);
}
3.1.3 注意事项
与手写的字符串转数字函数相比,stoi
方法的速度更慢(特别是在大数据量的情况下),主要有以下几个原因
- 错误检查:
stoi
函数会进行更多的错误检查。例如,它会检查字符串是否为有效的数字表示,是否超出了int
类型的范围,以及是否有非数字字符。这些检查增加了额外的运行时间 - 功能丰富:
stoi
函数提供了更多的功能,例如可以指定基数(默认为10),并且能够处理前导的空白字符。这些额外的特性也意味着更多的代码和可能的性能开销 - 异常处理:如果转换过程中发生错误(如格式错误或超出范围),
stoi
会抛出异常。异常处理通常比简单的错误检查要慢,因为它涉及到堆栈展开和异常对象的创建 - 标准库实现:
stoi
是标准库的一部分,它的实现可能考虑了通用性和可移植性,而不是绝对的性能。标准库的实现可能包含一些额外的代码以确保在各种平台上的一致行为
3.2 字符串数组转数字数组
vector<int> stringVectorToIntVector(vector<string> source) {
vector<int> result;
if (source.empty()) {
return result;
}
for (int i = 0; i < source.size(); i++) {
result.push_back(stringToInt(source[i]));
// result.push_back(stoi(source[i]));
}
return result;
}
4. 完整的示例代码
#include <iostream>
#include <cstring>
#include <vector>
#include <string>
#include <set>
#include <cmath>
#include <queue>
#include <iomanip>
#include <algorithm>
using namespace std;
/**
* 使用指定的分隔符将字符串拆分为字符串向量
*
* @param separator 分隔字符串
* @param source 被拆分的源字符串
* @return 返回一个包含拆分后字符串的向量
*
* 说明:
* 1. 如果源字符串为空,则直接返回空向量
* 2. 拆分过程从头开始,找到每次出现分隔符的位置,然后截取该位置前的子串添加到结果向量中
* 3. 如果找不到分隔符,则将整个源字符串添加到结果向量中,并结束循环
* 4. 每次找到分隔符后,更新起始位置为分隔符结束位置之后,以继续下一次拆分
* 5. 循环结束时,返回包含所有拆分字符串的向量
*/
vector<string> split(string separator, string source) {
vector<string> result;
if (source.length() == 0) {
return result;
}
int start = 0;
while (true) {
int position = source.find(separator, start);
if (-1 == position) {
position = source.length();
}
string temp = source.substr(start, position - start);
result.push_back(temp);
if (position == source.length()) {
break;
}
start = position + separator.length();
}
return result;
}
vector<string> split(char separator, string source) {
string str;
str += separator;
return split(str, source);
}
vector<string> split(char separator[], string source) {
string str = separator;
return split(str, source);
}
int stringToInt(string source) {
int result = 0;
// result = stoi(source);
for (int i = 0; i < source.length(); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
int stringToInt(char source[]) {
int result = 0;
// result = stoi(source);
for (int i = 0; i < strlen(source); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
vector<int> stringVectorToIntVector(vector<string> source) {
vector<int> result;
if (source.empty()) {
return result;
}
for (int i = 0; i < source.size(); i++) {
result.push_back(stringToInt(source[i]));
// result.push_back(stoi(source[i]));
}
return result;
}
void solve() {
string str;
getline(cin, str);
vector<string> stringVector = split(' ', str);
vector<int> intVector = stringVectorToIntVector(stringVector);
for (int i = 0; i < intVector.size(); i++) {
cout << intVector[i] << " ";
}
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
5. 巩固练习
5.1 A + B
题目链接:A + B
参考代码
#include <iostream>
#include <cstring>
#include <vector>
#include <string>
#include <set>
#include <cmath>
#include <queue>
#include <iomanip>
#include <algorithm>
using namespace std;
/**
* 使用指定的分隔符将字符串拆分为字符串向量
*
* @param separator 分隔字符串
* @param source 被拆分的源字符串
* @return 返回一个包含拆分后字符串的向量
*
* 说明:
* 1. 如果源字符串为空,则直接返回空向量
* 2. 拆分过程从头开始,找到每次出现分隔符的位置,然后截取该位置前的子串添加到结果向量中
* 3. 如果找不到分隔符,则将整个源字符串添加到结果向量中,并结束循环
* 4. 每次找到分隔符后,更新起始位置为分隔符结束位置之后,以继续下一次拆分
* 5. 循环结束时,返回包含所有拆分字符串的向量
*/
vector<string> split(string separator, string source) {
vector<string> result;
if (source.length() == 0) {
return result;
}
int start = 0;
while (true) {
int position = source.find(separator, start);
if (-1 == position) {
position = source.length();
}
string temp = source.substr(start, position - start);
result.push_back(temp);
if (position == source.length()) {
break;
}
start = position + separator.length();
}
return result;
}
vector<string> split(char separator, string source) {
string str;
str += separator;
return split(str, source);
}
vector<string> split(char separator[], string source) {
string str = separator;
return split(str, source);
}
int stringToInt(string source) {
int result = 0;
// result = stoi(source);
for (int i = 0; i < source.length(); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
int stringToInt(char source[]) {
int result = 0;
// result = stoi(source);
for (int i = 0; i < strlen(source); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
vector<int> stringVectorToIntVector(vector<string> source) {
vector<int> result;
if (source.empty()) {
return result;
}
for (int i = 0; i < source.size(); i++) {
result.push_back(stringToInt(source[i]));
// result.push_back(stoi(source[i]));
}
return result;
}
void solve() {
string str;
while (getline(cin, str)) {
vector<string> stringVector = split(' ', str);
vector<int> intVector = stringVectorToIntVector(stringVector);
int sum = 0;
for (int i = 0; i < intVector.size(); i++) {
sum += intVector[i];
}
cout << sum << endl;
}
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}
5.2 字符串排序
题目链接:字符串排序
参考代码
#include <iostream>
#include <cstring>
#include <vector>
#include <string>
#include <set>
#include <cmath>
#include <queue>
#include <iomanip>
#include <algorithm>
using namespace std;
/**
* 使用指定的分隔符将字符串拆分为字符串向量
*
* @param separator 分隔字符串
* @param source 被拆分的源字符串
* @return 返回一个包含拆分后字符串的向量
*
* 说明:
* 1. 如果源字符串为空,则直接返回空向量
* 2. 拆分过程从头开始,找到每次出现分隔符的位置,然后截取该位置前的子串添加到结果向量中
* 3. 如果找不到分隔符,则将整个源字符串添加到结果向量中,并结束循环
* 4. 每次找到分隔符后,更新起始位置为分隔符结束位置之后,以继续下一次拆分
* 5. 循环结束时,返回包含所有拆分字符串的向量
*/
vector<string> split(string separator, string source) {
vector<string> result;
if (source.length() == 0) {
return result;
}
int start = 0;
while (true) {
int position = source.find(separator, start);
if (-1 == position) {
position = source.length();
}
string temp = source.substr(start, position - start);
result.push_back(temp);
if (position == source.length()) {
break;
}
start = position + separator.length();
}
return result;
}
vector<string> split(char separator, string source) {
string str;
str += separator;
return split(str, source);
}
vector<string> split(char separator[], string source) {
string str = separator;
return split(str, source);
}
int stringToInt(string source) {
int result = 0;
// result = stoi(source);
for (int i = 0; i < source.length(); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
int stringToInt(char source[]) {
int result = 0;
// result = stoi(source);
for (int i = 0; i < strlen(source); i++) {
result = result * 10 + source[i] - '0';
}
return result;
}
vector<int> stringVectorToIntVector(vector<string> source) {
vector<int> result;
if (source.empty()) {
return result;
}
for (int i = 0; i < source.size(); i++) {
result.push_back(stringToInt(source[i]));
// result.push_back(stoi(source[i]));
}
return result;
}
void solve() {
string str;
while (getline(cin, str)) {
vector<string> stringVector = split(',', str);
sort(stringVector.begin(), stringVector.end());
for (int i = 0; i < stringVector.size(); i++) {
cout << stringVector[i];
if (i != stringVector.size() - 1) {
cout << ",";
}
}
cout << endl;
}
}
int main() {
std::ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int T = 1;
// cin >> T;
while (T--) {
solve();
}
return 0;
}