《STL 六大组件之容器探秘:深入剖析 string》

目录

  • 一、string 类简介
  • 二、string 类的常用接口
    • 1. 构造函数(constructor function)
    • 2. 与容量相关的接口(capacity)
    • 3. 与迭代器有关的接口(iterator)
    • 4. 与元素访问有关的接口(element access)
    • 5. 与内容修改有关的接口(modify)
    • 6. 与 string 对象操作有关的接口(operation)
    • 7. string 类的关系运算符重载接口
    • 8. string 类的输入输出
  • 三、与 string 类相关的精选习题
    • 1. 把字符串转换成整数(atoi)
    • 2. 字符串相加
    • 3. 仅仅反转字母
    • 4. 字符串中的第一个唯一字符
    • 5. 字符串最后一个单词的长度
    • 6. 验证回文串
    • 7. 反转字符串Ⅱ
    • 8. 反转字符串中的单词Ⅲ
    • 9. 字符串相乘

一、string 类简介

  string 是 C++ 标准库中的一个类,该类专门用来管理字符串。相比于 C 语言中使用字符数组来管理字符串,string 类使用方便且更加强大。
  不同于 C 风格字符串,string

二、string 类的常用接口

  string 类一共有一百多个接口,而常用的接口也就二十来个。所以需要牢牢掌握这常用的二十来个结构,剩下的接口在需要的时候会查询文档即可。

1. 构造函数(constructor function)

  以下是 C++ 标准库中 string 类的构造函数,上半部分是常用的构造函数,需要掌握且熟练运用;而下半部分不常用的构造函数只需了解即可。

(1)函数原型

// 构造函数// 常用的构造函数
string();  // 默认构造函数
string(const string& str);  // 复制构造函数
string(const char* str)  // 用 C 字符串 s 初始化
string(const char* s, size_t n);  // 用 C 字符串 s 的前 n 个字符初始化
string(size_t n, char c);  // 用 n 个字符 c 初始化// 不常用的构造函数
string (const string& str, size_t pos, size_t len = npos);  // 用 string 类对象 str 的下标为 pos 开始的长度为 len 的子串初始化
template <class InputIterator>  string  (InputIterator first, InputIterator last);  // 用迭代器[first, last)范围内的字符串初始化

(2)使用演示

// string 构造函数使用演示
void test1()
{// 默认构造函数string str1;cout << "str1 = " << str1 << endl;// 使用 C 字符串初始化string str2("apple");cout << "str2 = " << str2 << endl;// 复制构造函数string str3(str2);cout << "str3 = " << str3 << endl;// 用 C 字符串的前 n 个字符初始化string str4("aaabbb", 3);cout << "str4 = " << str4 << endl;// 用 n 个字符 c 初始化string str5(5, 'e');cout << "str5 = " << str5 << endl;
}

(3)运行结果
在这里插入图片描述

2. 与容量相关的接口(capacity)

  与 C 字符串相比 string 类的一个明显的优势在于它可以根据字符串的长度进行自动增容。而 C 字符串需要提前计算好字符串的长度而开辟相应大小的字符数组。

(1)函数原型

// 常用的与容量有关的接口
size_t size() const;  // 返回当前 string 类对象的长度(与其他 STL 组件保持一致)
size_t length() const;  // 返回当前 string 类对象的长度(未把 string 列为 STL 之前使用的成员函数)
void resize(size_t n);  // 把当前 string 对象的 size 修改至 n
void resize(size_t n, char c);  // 把当前 string 对象的 size 修改至 n,并把新增的字符全部设置为 c
size_t capacity() const;  // 返回当前 string 类对象的容量
void reserve(size_t n = 0);  // 把当前 string 类对象的容量修改为 n
bool empty() const;  // 判断当前 string 类对象是否为空,若为空返回 true,否则返回 false// 不常用的与容量有关的接口
size_t max_size() const;  // 返回 string 类对象的最大容量(可以容纳的最长字符数)
void clear();  // 把当前 string 类对象的内容清空,长度变为 0,容量不变
void shrink_to_fit();  // 请求字符串减小容量来适应 size 的大小

(2)使用演示

// string 类与容量有关的函数使用演示
void test2()
{string str1("C++");// 原 size 和 capacitycout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;cout << "Length: " << str1.length() << endl;// 修改 size 并指定字符str1.resize(5, 'a');cout << "\nstr1 = " << str1 << endl;cout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;// 修改 capacitystr1.reserve(18);cout << "\nstr1 = " << str1 << endl;cout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;// 清空并判断当前 string 对象是否为空str1.clear();cout << "\nstr1 = " << str1 << endl;cout << "Size: " << str1.size() << endl;cout << "Capacity: " << str1.size() << endl;if (str1.empty())cout << "Current string is empty.\n";
}

(3)运行结果
在这里插入图片描述

3. 与迭代器有关的接口(iterator)

  迭代器是所有 STL 容器都支持的一种访问手段,迭代器的实质可以是指针也可以是其他自定义类型。支持迭代器也就支持范围 for。

(1)函数原型

// 常用的迭代器接口
iterator begin();  // 返回普通 string 对象的开头迭代器
iterator end();  // 返回普通 string 对象的尾后迭代器
const_iterator begin() const;  // 返回 const string 对象的开头迭代器
const_iterator end() const;  // 返回 const string 对象的尾后迭代器
reverse_iterator rbegin();  // 返回普通 string 对象最后一个元素的迭代器
reverse_iterator rend();  // 返回普通 string 对象第一个元素前一个元素的迭代器
const_reverse_iterator rbegin() const;  // 返回 const string 对象最后一个元素的迭代器
const_reverse_iterator rend(); const  // 返回 const string 对象第一个元素前一个元素的迭代器// 还有几个前缀 c 的不常用的迭代器接口,用来返回 const 类型的迭代器,也就等价于上述四个返回 const 迭代器的接口。感兴趣的可以自己去查询一下文档
const_iterator cbegin() const noexcept;
const_iterator cend() const noexcept;
const_reverse_iterator crbegin() const noexcept;
const_reverse_iterator crend() const noexcept;

(2)使用演示
  对迭代器可以使用解引用操作符(*)获得当前位置的元素。下面展示可以访问 string 元素的几种方法。

// string 类迭代器相关函数使用演示
// string 类迭代器相关函数使用演示
void test3()
{string str("Good night, everyone !");// 使用迭代器正序遍历string::iterator it = str.begin();while (it != str.end()){cout << *it;++it;}cout << endl;// 使用迭代器逆序遍历string::reverse_iterator rit = str.rbegin();while (rit != str.rend()){cout << *rit;++rit;}cout << endl;// 使用范围 for 遍历(编译器在编译阶段会替换成迭代器)for (const auto& it : str)cout << it;cout << endl;// 普通 string 类对象可以使用 const 迭代器string::const_iterator cit = str.begin();// const string 类不可以使用普通迭代器const string str1("C++");//it = str1.begin();  // 报错cit = str1.begin();  // 正确,const 对象只能使用 const 迭代器
}

(3)运行结果
在这里插入图片描述

4. 与元素访问有关的接口(element access)

  string 类通过使用下标运算符访问其类对象的元素,当然还有其他几个不常用的接口;

(1)函数原型

// 常用的访问元素有关的接口
char& operator[](size_t pos);  // 返回普通 string 对象 pos 下标处的字符引用
const char& operator[](size_t pos) const;  // 返回 const string 对象 pos 下标出的字符引用// 不常用的访问元素有关的接口
// at 接口与上面两个函数的区别在于,at 检查下标时会发出异常,而下标运算符使用断言
char& at(size_t pos);
const char& at(size_t pos) const;
// 返回当前 string 对象的最后一个字符的引用
char& back();
const char& back() const;
// 返回当前 string 对象的第一个字符的引用
char& front();
const char& front() const;

(2)使用演示

// (4)string 类访问元素相关的接口
void test4()
{string str("little fool");// 使用下标运算符访问 str 的每个字符元素for (int i = 0; i < str.size(); ++i)cout << str[i];cout << endl;
}

(3)运行结果
在这里插入图片描述

5. 与内容修改有关的接口(modify)

  string 类修改内容的接口有很多个,但最常用的是 += 操作符重载,该操作符重载既可以在末尾拼接字符串,也可以在末尾拼接单个字符。

(1)函数原型

// 常用的 string 修改内容的接口
string& operator+=(const string& str);  // 在当前 string对象末尾拼接一个 string 对象 str
string& operator+=(const char* s);  // 在当前 string 对象末尾拼接一个 C 风格字符串 s
string& operator+=(char c);  // 在当前 string 对象末尾拼接一个字符 c
void push_back(char c);  // 在当前 string 对象末尾拼接一个字符 c
string& append(const string& str);  // 在当前 string 对象末尾拼接一个 string 对象
string& append(const char* s);  // 在当前 string 对象末尾拼接一个 C 风格字符串
string& append(size_t n, char c);  // 在当前 string 对象末尾拼接 n 个字符 c
// insert() 接口有多个重载,需要的时候需要查阅相关文档
string& insert(size_t pos, const string& str);  // 在当前 string 对象的下标 pos 处插入一个 string 对象 str
string& insert(size_t pos, const char& s);  // 在当前 string 对象的下标 pos 处插入一个 C 风格字符串
string& erase(size_t pos = 0, size_t len = npos);  // 删除当前 string 对象从 pos 位置开始的长度为 len 的子串
iterator erase(iterator p);  // 删除当前迭代器位置对应的字符,返回下一个字符的迭代器
void swap(string& str);  // 把当前 string 对象和 str 交换
void pop_back();  // 删除当前 string 对象的最后一个字符// 不常用的 string 修改内容的接口
iterator erase(iterator first, iterator last);  // 删除该迭代器区间的所有字符,返回 last 后一个位置的迭代器
// assign() 接口,给当前 string 对象重新赋值,但 string 类主要使用赋值运算符重载给 string 对象重新赋值
string& assign(const string& str);
string& assign(const char* s);
// replace() 接口,替换当前 string 对象的部分内容
string& replace(size_t pos, size_t len, const string& str);  // 从当前 string 对象的下标 pos 开始,使用 str 的前 len 个字符替换

(2)使用演示

// (5)string 类修改内容有关的接口
void test5()
{string str("Hello");// string 类的 += 操作符重载演示str += ' ';str += "world";string str1(" !");str += str1;cout << "str = " << str << endl;// 尾插和尾删str.push_back('A');cout << "str = " << str << endl;str.pop_back();cout << "str = " << str << endl;// 在任意位置插入string str2("A");str2.insert(1, "apple");cout << "str2 = " << str2 << endl;int end = (int)str2.size();str2.insert(end, ".");cout << "str2 = " << str2 << endl;// 在任意位置删除int len = (int)str2.size() - 1;str2.erase(1, len);cout << "str2 = " << str2 << endl;// 交换两个 string 对象string str3("Java");string str4("python");cout << "str3 = " << str3 << endl;cout << "str4 = " << str4 << endl;str3.swap(str4);cout << "str3 = " << str3 << endl;cout << "str4 = " << str4 << endl;
}

(3)运行结果
在这里插入图片描述

6. 与 string 对象操作有关的接口(operation)

  虽然 stirng 类对象不需要以空字符结尾,但是由于 C++ 兼容 C,所以其结尾依旧保留空字符,而使用 c_str() 接口,可以获得其 C 风格字符串的形式,也就是字符指针(char*)。

(1)函数原型

// 常用的与 string 对象操作有关的接口
const char* c_str() const;  // 返回当前 string 对象首字符的地址
size_t find(const string& str, size_t pos = 0) const;  // 在当前 string 对象的 pos 位置开始,寻找 stirng 对象 str 首次出现的位置
size_t find(const char* s, size_t pos = 0) const;  // 在当前 string 对象 pos 位置开始,寻找 C 风格字符串 s 首次出现的位置
size_t find(char c, size_t pos = 0) const;  // 在当前 string 对象 pos 位置开始,寻找字符 c
// 下面三个函数和前面三个 find 函数一致,只不过是从后往前查找
size_t rfind(const string& str, size_t pos = 0) const;
size_t rfind(const char* s, size_t pos = 0) const;
size_t rfind(char c, size_t pos = 0) const;// 不常用的与 string 对象操作相关的接口
string substr(size_t pos = 0, size_t len = npos) const;  // 返回当前 string 对象 pos 位置开始的长度为 len 的子串

(2)使用演示

// (6)与 string 对象操作相关的接口
void test6()
{string str("You are very good!");// 获取 C 风格字符串const char* s = str.c_str();cout << "str = " << str << endl;cout << "s  = " << s << endl;// 在 str 中查找字符size_t pos = str.find('a');for (size_t i = pos; i < str.size(); ++i)cout << str[i];cout << endl;// 在 str 中查找字符串pos = str.find("very");for (size_t i = pos; i < str.size(); ++i)cout << str[i];cout << endl;
}

(3)运行结果
在这里插入图片描述

7. string 类的关系运算符重载接口

  string 类重载了所有关系运算符,可以直接使用这些关系运算符对两个 string 对象进行比较,这些接口会按照字典顺序进行比较,返回布尔值。

(1)函数原型

// string 类关系运算符重载
bool operator<(const string& str) const;  // 当前 string 对象和 str 比较,若比 str 小则返回 true;否则返回 false
// 下面的接口同第一个接口
bool operator>(const string& str) const;
bool operator<=(const string& str) const;
bool operator>=(const string& str) const;
bool operator==(const string& str) const;
bool operator!=(const string& str) const;

(2)使用演示

// string 类关系运算符重载
void test7()
{string str1("C++");string str2("Java");// 开始比较cout << "str1 > str2 = " << (str1 > str2) << endl;cout << "str1 >= str2 = " << (str1 >= str2) << endl;cout << "str1 < str2 = " << (str1 < str2) << endl;cout << "str1 <= str2 = " << (str1 <= str2) << endl;cout << "str1 == str2 = " << (str1 == str2) << endl;cout << "str1 != str2 = " << (str1 != str2) << endl;
}

(3)运行结果
在这里插入图片描述

8. string 类的输入输出

  string 类的对象可以直接使用 cin 和 cout 搭配流提取运算符(>>)和流插入运算符(<<)进行输入和输出,因为 string 类重载了流插入和流提取运算符。string 类和 C 风格字符串在使用 cin 和流提取运算符进行输入时也只能输入一个单词,遇到空白就停止输入。
  如果需要获取多个单词的输入,就需要使用 istream 类的 getline 方法来获取一行输入。

(1)函数原型

istream& operator>>(istream& is, string& str);  // 流提取运算符重载
ostream& operator<<(ostream& os, const string& str);  // 流插入运算符重载

(2)使用演示

// (8)string 类的输入输出
void test8()
{string str;// 输入一个单词cout << "Please enter a word.\n";cin >> str;cout << "The word is " << str << endl;// 注意:上一次的输入会把换行符留在输入流,所以需要把遗留的换行符扔掉cin.get();// 输入一句话cout << "Please enter a sentence.\n";getline(cin, str);cout << "The sentence is " << str << endl;
}

(3)运行结果
在这里插入图片描述

三、与 string 类相关的精选习题

1. 把字符串转换成整数(atoi)

题目链接:link

题目描述: 请你来实现一个 myAtoi(string s) 函数,使其能将字符串转换成一个 32 位有符号整数(类似 C/C++ 中的 atoi 函数)。

函数 myAtoi(string s) 的算法如下:

  1. 读入字符串并丢弃无用的前导空格
  2. 检查下一个字符(假设还未到字符末尾)为正还是负号,读取该字符(如果有)。 确定最终结果是负数还是正数。 如果两者都不存在,则假定结果为正。
  3. 读入下一个字符,直到到达下一个非数字字符或到达输入的结尾。字符串的其余部分将被忽略。
  4. 将前面步骤读入的这些数字转换为整数(即,“123” -> 123, “0032” -> 32)。如果没有读入数字,则整数为 0 。必要时更改符号(从步骤 2 开始)。
  5. 如果整数数超过 32 位有符号整数范围 [−231, 231 − 1] ,需要截断这个整数,使其保持在这个范围内。具体来说,小于 −231 的整数应该被固定为 −231 ,大于 231 − 1 的整数应该被固定为 231 − 1 。
  6. 返回整数作为最终结果。

注意:
本题中的空白字符只包括空格字符 ’ ’ 。
除前导空格或数字后的其余字符串外,请勿忽略 任何其他字符。

示例:
在这里插入图片描述
提示:=

  1. 0 <= s.length <= 200
  2. s 由英文字母(大写和小写)、数字(0-9)、’ ‘、’+‘、’-’ 和 ‘.’ 组成

(1)解题思路:

  1. 首先使用 while 循环跳过前面的前导空格;
  2. 使用 if 语句检查下一个非空格字符是否为负号,如果不是负号则为正数;
  3. 开始读取数字,直到遇到第一个非数字字符。如果没有数字字符,则为 0;
  4. 如果整数超过 INT_MAX 则为 INT_MAX,如果整数小于 INT_MIN 则为 INT_MIN;
  5. 返回结果整数。

(2)解题代码:

class Solution {
public:int myAtoi(string str) {// 舍弃前面的前导空格int i = 0;while (' ' == str[i])++i;// 判断正负号int flag = 1;if ('-' == str[i]){flag = -1;++i;}else if ('+' == str[i]){flag = 1;++i;}// 开始计算int len = str.size();long long result = 0;while (i < len){// 是数字字符则加入计算if (isdigit(str[i])){result = result * 10 + str[i] - '0';// 判断计算结果if (result > INT_MAX){if (1 == flag)return INT_MAX;elsereturn INT_MIN;}}else  // 否则结束计算{break;}// 下一个字符++i;}// 返回计算结果return result * flag;}
};

2. 字符串相加

题目链接:link

题目描述:

  1. 给定两个字符串形式的非负整数 num1 和num2 ,计算它们的和并同样以字符串形式返回。
  2. 你不能使用任何內建的用于处理大整数的库(比如 BigInteger), 也不能直接将输入的字符串转换为整数形式。

示例:
在这里插入图片描述

提示:

  1. 1 <= num1.length, num2.length <= 104
  2. num1 和num2 都只包含数字 0-9
  3. num1 和num2 都不包含任何前导零

(1)解题思路:

  1. 参考数字的加法运算,从两个字符串的最后一个字符开始相加,分别计算当前位置和进位,然后把当前位置拼接到结果字符串中,直到其中一个字符串达到末尾;
  2. 把剩下没到末尾的字符串剩下的字符继续拼接到结果字符串中,最后把整个字符串逆置;
  3. 返回结果字符串。

(2)解题代码:

class Solution {
public:string addStrings(string num1, string num2) {// 所需变量string result;int i1 = num1.size() - 1, i2 = num2.size() - 1;int carry = 0;// 开始拼接while (i1 >= 0 || i2 >= 0){// 计算当前两位int n1 = (i1 >= 0? num1[i1--] - '0' : 0);int n2 = (i2 >= 0? num2[i2--] - '0' : 0);// 拼接result += (n1 + n2 + carry) % 10 + '0';// 计算进位carry = (n1 + n2 + carry) / 10;}// 如果两个字符串同时结束,但是 carry 不为 0if (carry)result += carry + '0';// 逆置结果字符串reverse(result.begin(), result.end());// 返回结果字符串return result;}
};

3. 仅仅反转字母

题目链接:link

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

  1. 所有非英文字母保留在原有位置。
  2. 所有英文字母(小写或大写)位置反转。
  3. 返回反转后的 s 。

示例:
在这里插入图片描述
提示:
4. 1 <= s.length <= 100
5. s 仅由 ASCII 值在范围 [33, 122] 的字符组成
6. s 不含 ‘"’ 或 ‘\’

(1)解题思路:

  1. 使用两个指针,分别指向字符串的首尾,当两个指针未相遇并且都指向字母字符时交换,当两个指针相遇或者错开时则结束;
  2. 返回交换完毕的字符串;

(2)解题代码:

class Solution {
public:string reverseOnlyLetters(string s) {int left = 0, right = s.size() - 1;// 开始交换while (left < right){// 左右寻找字母字符while (left < right && !isalpha(s[left]))++left;while (left < right && !isalpha(s[right]))--right;// 交换swap(s[left], s[right]);// 下一组++left;--right;}// 返回交换完成的字符串return s;}
};

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

题目链接:link

题目描述: 给定一个字符串 s ,找到 它的第一个不重复的字符,并返回它的索引 。如果不存在,则返回 -1 。

示例:
在这里插入图片描述
提示:

  1. 1 <= s.length <= 105
  2. s 只包含小写字母

(1)解题思路:

  1. 由于字符串只有小写字母,那么创建一个包含 26 个元素的 int 数组,对应 26 个小写字母的出现次数;
  2. 遍历一遍数组,计算每个字符出现的次数,再遍历一遍数组,找出第一个出现一次的字符;
  3. 返回对应的下标;

(2)解题代码:

class Solution {
public:int firstUniqChar(string s) {int times[26] = {0};// 计算每个字符出现的次数for (const auto& e : s){++times[e - 'a'];}// 找到第一个出现一次的字符int len = s.size();for (int i = 0; i < len; ++i){if (1 == times[s[i] - 'a'])return i;}// 没有找到返回 -1return -1;}
};

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

题目链接:link

题目描述: 对于给定的若干个单词组成的句子,每个单词均由大小写字母混合构成,单词间使用单个空格分隔。输出最后一个单词的长度。

输入描述:
在一行上输入若干个字符串,每个字符串代表一个单词,组成给定的句子。
除此之外,保证每个单词非空,由大小写字母混合构成,且总字符长度不超过 103

输出描述:
在一行上输出一个整数,代表最后一个单词的长度。

(1)解题思路:

  1. 创建一个 string 对象接受输入,每次输入一个单子并计算其长度,直到遇到换行符;
  2. 返回此时的长度,也就是最后一个单词的长度。

(2)解题代码:

// 头文件
#include <iostream>
#include <string>// using 声明
using std::string;
using std::cin;
using std::cout;
using std::endl;int main() {int len = 0;string str;// 输入单词并计算长度while (cin >> str){len = str.size();}// 输出最后一个单词的长度cout << len << endl;
}
// 64 位输出请用 printf("%lld")

6. 验证回文串

题目链接:link

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

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

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

示例:
在这里插入图片描述

提示:

  1. 1 <= s.length <= 2 * 105
  2. s 仅由可打印的 ASCII 字符组成

(1)解题思路:

  1. 首先遍历一遍字符串,把所有大写字母转换成小写字母,然后把所有字母数字字符拼接到一个新的字符串后头;
  2. 然后使用两个指针正着和倒着遍历来验证新的字符串是否为回文字符串;
  3. 返回验证的结果;

(2)解题代码:

class Solution {
public:bool isPalindrome(string s) {string tmp;// 小写转大写并把所有的字母数字字符拼接到新的字符串后面for (int i = 0; i < s.size(); ++i){if (isalnum(s[i])){tmp += tolower(s[i]);}}// 验证新的字符串int left = 0, right = tmp.size() - 1;while (left < right){// 判断当前这组字符if (tmp[left] != tmp[right]){return false;}// 下一组字符++left;--right;}// 是回文串return true;}
};

7. 反转字符串Ⅱ

题目链接:link

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

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

示例:
在这里插入图片描述

提示:

  1. 1 <= s.length <= 104
  2. s 仅由小写英文组成
  3. 1 <= k <= 104

(1)解题思路:

  1. while 循环,前后指针法,cur 指针往前走,pre 指针不动,当 cur 指针与 pre 指针的距离达到 2k-1 时,逆置前 k 个字符并更新 pre 指针的位置;
  2. 当 cur >= s.size() 时跳出循环,并根据此时 cur - pre 的值判断最后一组的情况;
  3. 返回逆置完成的字符串。

(2)解题代码:

// 逆置函数
void reverse(char* left, char* right)
{while (left < right){char tmp = *left;*left = *right;*right = tmp;// 下一组++left;--right;}
}class Solution {
public:string reverseStr(string s, int k) {// 使用前后指针遍历字符串,每当两个指针相隔 2k 时,反转int pre = 0, cur = 0;int len = s.size();while (cur < len){if (cur - pre == 2 * k - 1){// 反转前 k 个字符reverse(&s[pre], &s[pre + k - 1]);// 更新 prepre = cur + 1;}// cur 前进++cur;}// 判断最后的一段字符if (cur - pre < k)reverse(&s[pre], &s[cur - 1]);elsereverse(&s[pre], &s[pre + k - 1]);// 返回逆置完毕的字符串return s;}
};

8. 反转字符串中的单词Ⅲ

题目链接:link

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

示例:
在这里插入图片描述

提示:

  1. 1 <= s.length <= 5 * 104
  2. s 包含可打印的 ASCII 字符。
  3. s 不包含任何开头或结尾空格。
  4. s 里 至少 有一个词。
  5. s 中的所有单词都用一个空格隔开。

(1)解题思路:

  1. while 循环,前后指针法,pre 不动,cur 前进,当 cur 遇到空格时,逆置前面的单词后更新 pre,直到 cur 超出 s 末尾;
  2. 返回逆置完毕的 s。

(2)解题代码:

// 逆置函数
void reverse(char* left, char* right)
{while (left < right){char tmp = *left;*left = *right;*right = tmp;// 下一组++left;--right;}
}class Solution {
public:string reverseWords(string s) {int pre = 0, cur = 0, len = s.size();while (cur < len){// 如果 cur 是空格,则逆置前面的单词if (' ' == s[cur]){reverse(&s[pre], &s[cur-1]);// 更新 prepre = cur + 1;}// cur 前进++cur;}// 逆置最后一个单词reverse(&s[pre], &s[cur-1]);// 返回逆置的字符串return s;}
};

9. 字符串相乘

题目链接:link

题目描述: 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式。

示例:
在这里插入图片描述
提示:

  1. 1 <= num1.length, num2.length <= 200
  2. num1 和 num2 只能由数字组成。
  3. num1 和 num2 都不包含任何前导零,除了数字0本身。

(1)解题思路:

  1. 用其中一个字符串的每一位去乘以另一个字符串,然后把结果存在一个临时字符串中,最后加到结果字符串中去;
  2. 需要对不同的位进行补 0,个位不补,十位补一个 0,以此类推;
  3. 字符串的加法前面的习题中已经写过了,本题只是在其基础上进行了一些复杂的变化,难度没有什么提升;
  4. 返回最终的结果字符串。

(2)解题代码:
  本题思路没有问题,但是代码写的一般般,大家可以参考自己修改完善一下。

// 字符串相加函数
string add_string(const string& num1, const string& num2)
{// 所需变量int i1 = num1.size() - 1;int i2 = num2.size() - 1;int carry = 0;string result;// 开始相加while (i1 >= 0 || i2 >= 0){// 计算两个数字字符串当前位的值int n1 = (i1 >= 0 ? num1[i1] - '0' : 0);int n2 = (i2 >= 0 ? num2[i2] - '0' : 0);// 计算当前位int cur = n1 + n2 + carry;// 拼接当前位result += cur % 10 + '0';// 计算进位carry = cur / 10;// 下一组位--i1;--i2;} // 判断是否存在进位if (carry)result += carry + '0';// 逆置reverse(result.begin(), result.end());// 返回结果return result;
}class Solution {
public:string multiply(string num1, string num2) {// 若一个数为 0,则结果为 0if ("0" == num1 || "0" == num2)return "0";// 所需变量int i = num1.size() - 1;string result;// 用 num1 的每一位去乘以 num2while (i >= 0){char cur = num1[i];string cur_result;int carry = 0;// 当前位开始相乘for (int j = num2.size() - 1; j >= 0; --j){// 相乘int mul = (cur - '0') * (num2[j] - '0') + carry;// 计算本位并拼接到当前结果cur_result += mul % 10 + '0';// 计算进位carry = mul / 10;}// 补上进位if (carry)cur_result += carry + '0';// 逆置当前结果reverse(cur_result.begin(), cur_result.end());// 补零int n = num1.size() - i - 1;while (n--)cur_result += '0';// 把当前结果加到最终结果result = add_string(result, cur_result);// 下一位--i;}// 返回相乘的结果return result;}
};

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

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

相关文章

Unreal5从入门到精通之在编辑器中更新 UserWidgets

前言 在虚幻中创建越来越复杂和灵活的 UserWidget 蓝图时,一个问题是它们在编辑器中的外观与它们在游戏中的最终外观可能有很大不同。 库存面板示例 假设你想创建一个通用的库存显示小部件。我们可以在整个 UI 中使用它,无论我们需要在哪里显示某些内容。 标题,描述所显示…

计算机网络-OSI七层参考模型与数据封装,网络安全零基础入门到精通实战教程!

目录 一、网络 1、网络的定义 2、网络的分类 3、网络的作用 4、网络的数据传输方式 5、网络的数据通讯方式 二、OSI七层参考模型 1、网络参考模型定义 2、分层的意义 3、分层与功能 4、TCP\IP五层模型 三、参考模型的协议 1、物理层 2、数据链路层 3、网络层 4…

Python正则替换终极指南:用re.sub玩转字符串魔法

Python正则替换终极指南&#xff1a;用re.sub玩转字符串魔法 一、为什么re.sub是文本处理的瑞士军刀&#xff1f; 在Python的re模块中&#xff0c;re.sub()的周下载量突破5800万次&#xff08;2025年PyPI数据&#xff09;&#xff0c;它实现了&#xff1a; &#x1f4cd; 模…

gen_gauss_filter用于检测带方向的线条

目录 一、核心参数分析 1.1 方向覆盖范围 1.2 滤波器方向带宽 二、角度配置建议 三、参数选择依据 四、实施建议 五、模拟图测试(项目图档不好直接分享) 5.1 模拟图制作 5.2 检测伪代码 在Halcon中使用高斯滤波器检测多方向线条时,角度参数的选取需要综合考虑滤波…

C++17 中的 std::to_chars 和 std::from_chars:高效且安全的字符串转换工具

文章目录 1. 传统转换方法的局限性2. std::to_chars&#xff1a;数值到字符串的高效转换函数原型&#xff1a;返回值&#xff1a;示例代码&#xff1a;输出&#xff1a; 3. std::from_chars&#xff1a;字符串到数值的高效解析函数原型&#xff1a;返回值&#xff1a;示例代码&…

深入学习解析:183页可编辑PPT华为市场营销MPR+LTC流程规划方案

华为终端正面临销售模式转型的关键时刻&#xff0c;旨在通过构建MPRLTC项目&#xff0c;以规避对运营商定制的过度依赖&#xff0c;并探索新的增长路径。项目核心在于建设一套全新的销售流程与IT系统&#xff0c;支撑双品牌及自有品牌的战略发展。 项目总体方案聚焦于四大关键议…

Python正则表达式处理中日韩字符过滤全解析

Python正则表达式处理中日韩字符过滤全解析 一、核心原理&#xff1a;Unicode字符范围定位 中日韩字符在Unicode中的分布&#xff1a; 中文&#xff1a;\u4e00-\u9fff&#xff08;基本区&#xff09; \u3400-\u4dbf&#xff08;扩展A区&#xff09; \U00020000-\U0002a6df…

基于WOA鲸鱼优化的WSN网络最优节点部署算法matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 5.完整程序 1.程序功能描述 鲸鱼优化算法&#xff08;WOA&#xff09;是一种模拟座头鲸捕食行为的元启发式优化算法。其主要原理基于座头鲸独特的 “气泡网” 捕食策略&#xff0c;通过数学模…

【数据分析】3 数据分析成长之路

职业发展路径&#xff1a; 向上发展&#xff08;技术方向&#xff09;&#xff1a;可以详细说明成为数据科学家或专家所需的具体技能和步骤&#xff0c;包括学习的算法、工具等。向下发展&#xff08;业务方向&#xff09;&#xff1a;可以探讨结合业务知识的具体领域&#xff…

excel导入Mysql中时间格式异常

问题描述&#xff1a; 当使用xls/xlsx/csv导入mysql中&#xff0c;如果列是时间类型比如excel表中显示2024/02/20 09:18:00&#xff0c;导入后时间可能就会变成1900-01-01 09:18:00这样。 问题原因&#xff1a; 这是由于excel表中和数据库中的时间类型不匹配导致。 问题解决…

async checkpointing

Reducing Model Checkpointing Times by Over 10x with PyTorch Distributed Asynchronous Checkpointing | PyTorch 最初来源&#xff1a;IBM Research 核心思想&#xff1a;GPU->CPU&#xff0c;用的是blocking&#xff1b;CPU->Disk&#xff0c;用的是异步不阻塞训练…

Nginx稳定版最新1.26.2源码包安装【保姆级教学】

Nginx安装及配置 开源Nginx官网地址(https://nginx.org) Nginx源码包下载地址(https://nginx.org/en/download.html) Mainline version 主线版本 Stable version 稳定版本 Legacy versions 陈旧版本 下载Nginx源码文件 curl -O https://nginx.org/download/nginx-1.26.2.t…

在ubuntu上用Python的openpyxl模块操作Excel的案例

文章目录 安装模块读取Excel数据库取数匹配数据和更新Excel数据 在Ubuntu系统的环境下基本职能借助Python的openpyxl模块实现对Excel数据的操作。 安装模块 本次需要用到的模块需要提前安装(如果没有的话) pip3 install openpyxl pip3 install pymysql在操作前&#xff0c;需…

GeoHD - 一种用于智慧城市热点探测的Python工具箱

GeoHD - 一种用于智慧城市热点探测的Python工具箱 详细原理请参考&#xff1a;Yan, Y., Quan, W., Wang, H., 2024. A data‐driven adaptive geospatial hotspot detection approach in smart cities. Trans. GIS tgis.13137. 代码下载&#xff1a;下载 1. 简介 在城市数据…

16 中介者(Mediator)模式

中介者模式 1.1 分类 &#xff08;对象&#xff09;行为型 1.2 提出问题 实现一个机场的起飞管理系统&#xff0c;如果由驾驶员们讨论谁先飞&#xff0c;后果可能是灾难性的。 1.3 解决方案 用一个中介对象来封装一系列的对象交互。中介者使各对象不需要显式地相互引用&a…

最薄的机身,游最深的海

经济全球化的潮流&#xff0c;势不可挡。 “技术、通讯和全球化&#xff0c;正在消除传统的地理和经济边界&#xff0c;使竞争环境趋于平等。”《世界是平的》作者托马斯弗里德曼预言的"扁平化竞争"正加速演进。 在高端智能手机战场&#xff0c;一场由中国企业主导…

【HarmonyOS Next】鸿蒙监听手机按键

【HarmonyOS Next】鸿蒙监听手机按键 一、前言 应用开发中我们会遇到监听用户实体按键&#xff0c;或者扩展按键的需求。亦或者是在某些场景下&#xff0c;禁止用户按下某些按键的业务需求。 这两种需求&#xff0c;鸿蒙都提供了对应的监听事件进行处理。 onKeyEvent 默认的…

IC设计版图中GDS2文件格式详解

版图中GDS2文件格式详解 目录 什么是GDS2文件&#xff1f;GDS2文件的历史与发展GDS2文件的结构解析GDS2文件的核心元素GDS2 vs. OASIS: 核心差异如何生成与查看GDS2文件&#xff1f;GDS2文件的局限性应用场景总结 什么是GDS2文件&#xff1f; GDS2&#xff08;Graphic Data …

KMP算法:字符串匹配的智慧跳跃

文章目录 起因&#xff1a;暴力法的致命缺陷暴力搜索的局限性 KMP核心思想&#xff1a;避免重复理解前缀表&#xff08;PMT&#xff09;不匹配时的回退机制代码&#xff1a;高效字符串匹配补充&#xff1a;next表和PMT表 暴力法 vs KMP总结&#xff1a;KMP 是如何改变游戏规则的…

上位机知识篇---setuptools

文章目录 前言简介一、核心功能1.依赖管理自动安装依赖版本约束额外依赖组命令行工具插件系统 2.开发模式安装3.资源文件管理4.Egg 分发&#xff08;已逐渐被 Wheel 取代&#xff09;5.命名空间包 二、基础用法1. 项目结构示例2. 配置文件 setup.cfg3. setup.py 最小化示例&…