C++ 三级难点讲解:二维数组与字符串综合
在 GESP/等级考试三级中,二维数组(矩阵) 和字符数组(字符串) 是难度跃升最大的两个模块。它们不像一级二级那样只考简单循环,而是要求你用数组去模拟复杂逻辑。
难度定位
一级~二级:一维数组、简单字符串遍历
三级核心难点:
二维数组的行列遍历、矩阵变换(转置、旋转、蛇形)
字符数组char str[]与cstring函数(strlen,strcpy,strcmp)
字符串与数组/数学的综合题
四级及以上:结构体、多级指针、简单算法
整体来说,三级是从"写代码"到"用数据结构建模"的转折点,二维数组是分水岭。
下面我整理了两道三级常考的经典题型,帮你突破这个难点:
1. 二维数组:矩阵转置(行列互换)
问题描述
给定一个 n×m 的矩阵,输出它的转置矩阵(第 i 行第 j 列变成第 j 行第 i 列)。
输入格式
第一行两个整数 n, m(均不超过 100)。
接下来 n 行,每行 m 个整数。
输出格式
输出转置后的矩阵,共 m 行,每行 n 个整数。
样例
输入:
3 2
1 2
3 4
5 6
输出:
1 3 5
2 4 6
题意回顾
原矩阵 3 行 2 列 → 转置后变成 2 行 3 列。
本质是:a[i][j]→ 放到b[j][i]的位置。
核心思路:双重循环 + 下标互换
开一个原矩阵a[n][m]和一个转置矩阵b[m][n]
遍历原矩阵:对每个a[i][j],把它赋值给b[j][i]
输出b矩阵
关键理解:i是行号,j是列号。转置就是把"行变列、列变行",所以下标互换。
代码逐行解析
#include <bits/stdc++.h>
using namespace std;
int main(){
int n, m;
cin >> n >> m;
int a[105][105], b[105][105]; // 开大一点防止越界
// 读入原矩阵
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
cin >> a[i][j];
}
}
// 转置:a[i][j] → b[j][i]
for(int i = 0; i < n; i++){
for(int j = 0; j < m; j++){
b[j][i] = a[i][j]; // 核心:下标互换
}
}
// 输出转置后的矩阵(m 行 n 列)
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
cout << b[i][j] << " ";
}
cout << endl;
}
return 0;
}
复杂度
时间:O(n×m),遍历两遍矩阵
空间:O(n×m),开了一个辅助矩阵
常见错误提醒
错误后果
输出时还是for i<n, j<m输出维度和原矩阵一样,没转置
b[i][j] = a[i][j]没互换下标,只是复制
数组开a[100][100]但输入 100×100下标从 0 到 99 刚好,但如果用 1~100 就越界
忘记换行cout << endl输出格式错误,全部挤一行
🧠 进阶变体
原地转置? 只有方阵(n=m)才能原地,用swap(a[i][j], a[j][i])只遍历上三角
矩阵旋转 90°? 转置后再每行反转,或者直接用b[j][n-1-i] = a[i][j]
蛇形填数? 按方向模拟,需要dir数组控制上下左右
2. 字符数组与字符串:单词计数
问题描述
输入一行可能包含空格的字符串(英文句子),统计其中有多少个单词。单词之间用一个或多个空格隔开。
输入格式
一行字符串,长度不超过 1000。
输出格式
一个整数,表示单词个数。
样例
输入:Hello world from C++
输出:4
题意回顾
给定一行带空格的字符串,单词由字母组成,词间可能多个空格。统计单词总数。
核心思路:状态切换法(或cin >> string流)
方法一:用cin >> string逐个读单词(最简单)
cin遇到空格自动截断,每次读一个单词,读几次就是几个单词。
方法二:字符数组遍历(三级常考,理解原理)
遍历字符数组,遇到"从空格变成非空格"的瞬间,说明新单词开始了 → 计数+1。
代码逐行解析(方法一:推荐,简洁)
#include <bits/stdc++.h>
using namespace std;
int main(){
string s;
int cnt = 0;
// cin >> s 会自动跳过前导空格,每次读一个单词
while(cin >> s){
cnt++;
}
cout << cnt;
return 0;
}
方法二:字符数组遍历(理解底层逻辑)
#include <bits/stdc++.h>
using namespace std;
int main(){
char str[1005];
cin.getline(str, 1005); // 读一整行(含空格)
int len = strlen(str);
int cnt = 0;
bool inWord = false; // 标记当前是否在单词内
for(int i = 0; i < len; i++){
if(str[i] != ' ' && !inWord){
// 从空格进入单词 → 新单词开始
cnt++;
inWord = true;
}
else if(str[i] == ' '){
// 遇到空格 → 离开单词
inWord = false;
}
}
cout << cnt;
return 0;
}
逐行解析(方法二)
cin.getline(str, 1005)— 读取整行,包括空格,存到字符数组
strlen(str)— 算实际长度(不含\0)
inWord是状态标记:
false表示当前在空格区
true表示正在单词内部
当str[i]不是空格且inWord == false→ 刚进入新单词 →cnt++
复杂度
时间:O(n),n ≤ 1000
空间:O(1) 或 O(n)(存字符串)
常见错误提醒
错误后果
用cin >> str而不是getline只能读第一个单词,遇空格就停
多个连续空格时if(str[i]!=' ')直接cnt++一个单词里每个字母都算一次
strlen用在未初始化的char[]找不到\0,返回随机值
忘记#include <cstring>strlen编译报错
🧠 延伸变体
统计每个单词的长度? 进入单词时记起始位置,离开时算差值
找最长的单词? 边计数边记录最大长度
字符串反转(单词不反)? 先整体反转,再逐个单词反转回来
大数加法? 两个字符串逐位相加,进位存数组
📌 一句话总结
模块核心考点一句话口诀
二维数组双重循环、下标映射"i 是行 j 是列,转置互换就完事"
字符数组strlen/strcpy/strcmp、状态遍历"空格切单词,状态记进出"
字符串转数字s[i]-'0'"位数超 19,一律当字符串"
掌握这两道例题的套路,三级的数组和字符串大题基本都能拿到分。二维数组多画图模拟,字符串多手动追踪状态,考试时不慌。
制作不易,如果对你有帮助,点个赞吧 😁