课程 · C++ 速查 · 字符串 string

字符串 string 例题与练习

把「字符也是整数」这半句话用熟 —— 它是开计数数组的前提,也是整组里唯一真正要转的那个弯。

2 道例题3 道练习5 份可运行代码回速查看这一组的 9 条 →
怎么用这一页

例题给全套:题面、样例、参考代码、以及一步步的讲解。先自己读题想一遍, 再看代码,最后对着讲解核一遍自己想的对不对。

练习只给题面、样例和一句提示,答案是折叠起来的 —— 请先自己写一版跑通了再展开。写不出来也别直接看答案,先回速查那一组找找。

每份代码都能直接点运行:输入框里已经填好了样例输入, 把它改一改再跑,是这一页最值钱的用法。

先把地基摆一遍

3 段

string 就是「一排 char」,而且它自己知道自己有多长。你可以像数组一样 s[0]s[i] 地取,也可以整个拿来比大小、拼接、当函数的返回值 —— 这比 C 语言那种 char s[100] 省心得多。竞赛里除非卡常,一律用它。

★ 最该先记牢的一条是「字符也是整数」char 在 C++ 里本来就是一个小整数,'a' 是 97、'0' 是 48。所以 c - '0' 把字符 '7' 变成数字 7,c - 'a' 把字母变成 0~25 的编号 —— 后者是开计数数组 cnt[26] 的前提,第 47 章起几乎每道字符串题都要用。

⚠ 而字符串比大小是字典序,不是数值:"100" < "99"的(因为 '1' < '9')。第 44 章高精度里那个「比大小要先比位数」的坑,根子就在这儿。

例题(2 道)

每道都给全套:题面 → 样例 → 参考代码 → 一步步的讲解。

例题 ① 每个字母出现了几次

strEx1.cpp

给一个只由小写字母组成的字符串,统计每个字母各出现了多少次,按字母表顺序输出。

输入格式一行一个字符串 s(只含小写字母,长度 ≤ 10⁵)。
输出格式若干行,每行一个字母和它出现的次数,用空格隔开;没出现过的字母不输出。

输入

hello

输出

e 1
h 1
l 2
o 1
hello 里 e 一个、h 一个、l 两个、o 一个 —— 按字母序输出。
先想一想

开一个长度 26 的计数数组,然后……字母怎么变成下标?这一步是这一组最该记牢的一句话。

strEx1.cpp参考代码
// 例题 ①「每个字母出现了几次」—— 字符串组
//
// 整页最该记牢的一句话在这里兑现:**字符也是整数。**
// 'a' 是 97、'b' 是 98 …… 所以 c - 'a' 把 26 个字母变成 0~25 的编号,
// 而这正是「开一个长度 26 的计数数组」的前提。第 47 章起几乎每道字符串题都要用。
#include <bits/stdc++.h>
using namespace std;
int main() {
string s;
cin >> s; // >> 读到空格/换行就停 —— 它读的是「一个词」
int cnt[26] = {0}; // 26 个格子,全 0
for (char c : s) cnt[c - 'a']++; // 'h' - 'a' = 7 ⇒ 第 7 个格子加一
for (int i = 0; i < 26; i++)
if (cnt[i] > 0)
cout << (char)('a' + i) << ' ' << cnt[i] << "\n";
// 反过来:编号 + 'a' 再转回 char,就是那个字母本身
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果
这段代码是怎么想出来的
  1. char 在 C++ 里本来就是一个小整数(它存的是 ASCII 码):'a' 是 97、'b' 是 98、'h' 是 104。
  2. 于是 c - 'a' 就把字母变成 0 ~ 25 的编号:'h' - 'a' = 104 − 97 = 7。⇒ cnt[c - 'a']++ 读成「第 7 个格子加一」。
  3. 反过来也成立:(char)('a' + i) 把编号变回字母。⚠ 那个 (char) 不能省 —— 不转的话 cout 会把它当成一个数字打出来。
  4. for (char c : s) 就能把串里每个字符依次取出来 —— string 在这方面和 vector 一模一样。
记住这一句

这条 c - 'a' 是第 47 章起几乎每道字符串题的第一句。同一个道理的另一半是 c - '0':它把字符 '7' 变成数字 7。

用到的写法:c - '0' c - 'a' string s; cin >> s; s.size() / s[i] / s.back()

例题 ② "100" 到底比 "99" 大还是小

strEx2.cpp

给两个不含前导零的数字串 ab。请输出三样东西:

a 的各位数字之和;② 直接用 a < b 比较(也就是字典序)的结果;③ 按数值大小比较 a < b 的结果。

⚠ 两个串都可能很长(长度 ≤ 18),但这道题不需要把它们转成数字。

输入格式一行两个数字串 ab
输出格式三行,分别是各位数字之和、字典序比较的结果(0 或 1)、数值比较的结果(0 或 1)。

输入

100 99

输出

1
1
0
"100" < "99"的(第一位 '1' < '9' 就分出高下了),而 100 < 99 是假的。⇒ 这两件事根本不是一回事。
先想一想

第 ② 问一行就能写完。难的是第 ③ 问:两个数字串怎么按数值比大小?(想想什么时候一眼就能看出谁大。)

strEx2.cpp参考代码
// 例题 ②「"100" 到底比 "99" 大还是小」—— 字符串组
//
// 字符串的 < 是**字典序**:逐位比,第一个不同的位说了算。
// 于是 "100" < "99" 是**真**的(因为 '1' < '9'),而 100 < 99 是假的。
// 第 44 章高精度那句「比大小要先比位数」,根子就在这儿。
#include <bits/stdc++.h>
using namespace std;
// 按「数值」比较两个无前导零的数字串:先比位数,位数一样再比字典序
bool numLess(const string &a, const string &b) {
if (a.size() != b.size()) return a.size() < b.size();
return a < b;
}
int main() {
string a, b;
cin >> a >> b;
int sum = 0;
for (char c : a) sum += c - '0'; // '7' - '0' = 7:字符 → 它代表的数字
cout << sum << "\n";
cout << (a < b) << "\n"; // 字典序
cout << numLess(a, b) << "\n"; // 数值
return 0;
}
点一下即可编辑
输入(stdin)
输出
点「运行 ▶」看结果
这段代码是怎么想出来的
  1. 各位数字之和:for (char c : a) sum += c - '0'; —— 还是那句「字符也是整数」。
  2. 字符串的 <字典序:逐位比,第一个不同的位说了算;都相同时短的排前面。所以 "abc" < "abd""ab" < "abc"
  3. 按数值比就得自己写:先比位数(没有前导零 ⇒ 位数多的一定大),位数一样时字典序和数值恰好一致,这时才能直接用 <
  4. ⚠ 那个函数的参数写成 const string &a —— 传字符串一律这么写:少了 & 就是每次调用都把整个串复制一遍。
记住这一句

第 44 章高精度里那句「比大小要先比位数」,根子就在这儿。⇒ 而更一般的一句话是:字符串的 < 和数字的 < 是两个不同的东西,同名而已。

用到的写法:s == t s < t c - '0' c - 'a' s.size() / s[i] / s.back()

练习(3 道)

答案折叠着,先自己写一版。

练习 ① 是不是回文

strTry1.cpp

给一个字符串,判断它正着读和倒着读是不是一样的。

输入格式一行一个字符串 s(不含空格,长度 ≤ 10⁵)。
输出格式是回文输出 Yes,否则输出 No

输入

abcba

输出

Yes
abcba 正着倒着都一样。
提示

从两头往中间走。⚠ 「倒数第 i 个」的下标是多少?(这是全书最常见的差一,写错了不一定报错,只是答案不对。)另外:循环要走到哪儿才停?走满全程会发生什么?

strTry1.cpp参考答案

用到的写法:s.size() / s[i] / s.back() string s; cin >> s;

练习 ② 大小写互换

strTry2.cpp

给一个字符串,把其中的小写字母变成大写、大写字母变成小写,其余字符原样不动,然后输出。

输入格式一行一个字符串 s(不含空格,长度 ≤ 10⁵)。
输出格式一行,变换之后的字符串。

输入

Hello2World

输出

hELLO2wORLD
Hello2WorldhELLO2wORLD,中间那个数字 2 原样留着。
提示

'a' 是 97、'A' 是 65 —— 差正好 32。⚠ 而这道题要串里的字符:范围 for 写成 for (char c : s) 改的是谁?

strTry2.cpp参考答案
记住这一句

这道题真正的考点是那个 &for (char &c : s) 改的才是串本身。少一个 &,程序照样跑、照样不报错,只是什么都没变。

用到的写法:c - '0' c - 'a' s.size() / s[i] / s.back()

练习 ③ 所有长度为 k 的子串

strTry3.cpp

给一个字符串 s 和一个正整数 k,请把 s 里所有长度为 k 的连续子串按出现顺序逐行输出。

⚠ 如果 ks 还长,什么都不要输出。

输入格式一行一个字符串 s 和一个整数 k1 ≤ k ≤ 100)。
输出格式若干行,每行一个子串。

输入

abcd 2

输出

ab
bc
cd
abcd 长度为 2 的子串有 abbccd 三个。
提示

截一段用 s.substr(i, len)。⚠ 第二个参数是长度,不是结束下标。起点 i 最大能取到哪儿?(想想最后一个子串是从哪儿开始的。)

strTry3.cpp参考答案
记住这一句

把循环条件写成 i + k <= n 而不是 i <= n - k,就不用担心 n - k 变成负数 ——和上一组那条「别做减法」是同一个手势。

用到的写法:s.substr(i, len) s.size() / s[i] / s.back()