二维数组与字符串¶
二维数组:一排房间变成一栋楼¶
一维数组是一排房间,二维数组就是一栋楼:每层有若干房间,用「楼层 + 房号」定位:
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
cout << a[1][2] << endl; // 第 1 行第 2 列:7(行列都从 0 数)
遍历用双重循环:外层扫行、内层扫列:
for (int i = 0; i < 3; i++) { // 每一行
for (int j = 0; j < 4; j++) { // 行内每一列
cout << a[i][j] << " ";
}
cout << endl;
}
经典应用:矩阵题目(读入矩阵、输出转置、求对角线之和)和棋盘类题目(井字棋、迷宫地图)。比如迷宫题里 mp[i][j] 表示第 i 行第 j 列是空地还是墙——第 7 章搜索会大量使用。
行列别搞反
a[i][j] 是先行后列。写迷宫代码时把行列弄反,程序不会报错但答案离谱,
调试时先检查是不是这个问题。
string:字符串入门¶
string 是 C++ 自带的字符串类型,比字符数组好用得多:
#include <iostream>
#include <string> // string 需要
using namespace std;
int main() {
string name;
cin >> name; // 读入一个单词(空格/回车结束)
cout << "你好, " << name << "!" << endl;
cout << "长度: " << name.size() << endl;
return 0;
}
常用操作一览:
| 操作 | 写法 | 说明 |
|---|---|---|
| 长度 | s.size() 或 s.length() |
字符个数 |
| 取单个字符 | s[i] |
和数组一样,下标从 0 开始 |
| 比较 | s1 < s2 |
按字典序,直接用比较符 |
| 拼接 | s1 + s2 |
加号直接连起来 |
| 判断相等 | s1 == s2 |
C 里要 strcmp,C++ 直接 == |
| 截取 | s.substr(pos, len) |
从 pos 开始取 len 个字符 |
遍历字符串:回放数位拆分¶
统计字符串中数字字符的个数:
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
int cnt = 0;
for (int i = 0; i < (int)s.size(); i++) {
if (s[i] >= '0' && s[i] <= '9') cnt++; // 是数字字符
}
cout << cnt << endl;
return 0;
}
记住两个关键事实:
- 字符可以直接比大小:
s[i] >= '0'用的是字符的编码值('0'是 48,'9'是 57,连续) s[i] - '0'把数字字符变成对应整数:'8' - '0'得8——字符转数字的唯一公式
cin 读不了带空格的一句话
cin >> s 遇空格就停。读整行用 getline(cin, s);。
注意它常和 cin >> 混用出坑:cin >> n 后立即 getline 会读到残留的换行,
中间加一句 getchar();(或 cin.ignore();)吃掉换行即可。
综合练习:回文判断¶
判断一个字符串是不是回文(正读反读一样,如 level):
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
bool ok = true;
int len = s.size();
for (int i = 0; i < len / 2; i++) {
if (s[i] != s[len - 1 - i]) { // 首尾对称位置比较
ok = false;
break;
}
}
cout << (ok ? "yes" : "no") << endl;
return 0;
}
用到了首尾指针思想:一个从前往后、一个从后往前,对称检查。这个技巧后面还会见到。
例题 1:矩阵对角线之和¶
题目描述 读入一个 \(n \times n\) 的矩阵,输出主对角线(从左上到右下)上所有数之和。
输入格式 第一行一个整数 \(n\);接下来 \(n\) 行,每行 \(n\) 个整数。
输出格式 一个整数,主对角线元素之和。
数据范围 \(1 \le n \le 100\),\(|a_{ij}| \le 1000\)。
样例输入 / 样例输出
思路分析 观察主对角线上的元素:\((0,0)\)、\((1,1)\)、\((2,2)\)……规律是行号 = 列号。所以根本不用双重循环去遍历,单循环取 \(a[i][i]\) 即可。
参考代码
#include <iostream>
using namespace std;
int a[105][105];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cin >> a[i][j];
}
}
long long sum = 0;
for (int i = 0; i < n; i++) {
sum += a[i][i]; // 行号 = 列号,就是主对角线
}
cout << sum << endl;
return 0;
}
例题 2:大小写转换¶
题目描述 读入一个不含空格的字符串,把其中所有小写字母变成大写(其他字符原样保留)后输出。
输入格式 一行一个字符串(长度不超过 100)。
输出格式 转换后的字符串。
数据范围 字符串长度 \(\le 100\),可能包含字母、数字、标点。
样例输入 / 样例输出
思路分析 小写字母的编码比对应的大写字母恰好大 32('a' 是 97,'A' 是 65)。所以 c - 32(或写成更可读的 c - 'a' + 'A')就能变小写为大写。遍历字符串逐字符判断即可。
参考代码
#include <iostream>
#include <string>
using namespace std;
int main() {
string s;
cin >> s;
for (int i = 0; i < (int)s.size(); i++) {
if (s[i] >= 'a' && s[i] <= 'z') {
s[i] = s[i] - 'a' + 'A'; // 或写作 s[i] -= 32;
}
}
cout << s << endl;
return 0;
}
同款题:洛谷 P5733「自动修正」。另外 C++ 也自带转换函数 toupper(s[i])(需 #include <cctype>),学会原理后再认识它。
动手试一试¶
- 读入 3×3 矩阵,输出它的转置(行列互换)
- 读入一个字符串,统计大写字母、小写字母、数字各有多少个
- 读入单词,把其中所有小写字母变成大写输出(提示:
c - 32或c - 'a' + 'A') - 挑战:读入一句话(
getline),统计其中单词的个数(连续字母算一个单词)
恭喜,CSP-J 入门篇前 3 章毕业! 下一章把代码模块化:第 4 章 · 函数与递归。