补充知识:
模拟题的解题步骤
- 仔细阅读题目:
- 理解每个规则和约束条件
- 明确输入输出格式
- 设计数据结构
- 选择合适的数据结构存储状态
- 考虑如何高效访问和更新数据
- 实现核心逻辑
- 按照问题描述逐步实现
- 处理好状态转移
- 处理边界情况
- 考虑各种极端情况
- 添加必要的错误处理
- 测试验证
- 用样例测试代码
- 检查边界情况
【1】258. 各位相加
日期:10.6
2.类型:数学,数论,模拟
3.方法一:模拟(一次题解)
计算一个整数的各位相加的做法是,每次计算当前整数除以 10 的余数得到最低位数,将最低位数加到总和中,然后将当前整数除以 10。重复上述操作直到当前整数变成 0,此时的总和即为原整数各位相加的结果。
关键代码:
cpp
while(num>=10){
int sum=0;
while(num>0){
sum+=num%10;
num/=10;
}
num=sum;
}
日期:10.7
2.类型: 数学,字符串模拟
3.方法一:模拟(一次题解)
如果 i 是 3 的倍数,则将 "Fizz" 拼接到 answer[i];
如果 i 是 5 的倍数,则将 "Buzz" 拼接到 answer[i];
如果 answer[i] 为空,则 i 既不是 3 的倍数也不是 5 的倍数,将 i 拼接到 answer[i]。
关键代码:
cpp
vector<string> answer;
for(int i=1;i<=n;i++){
string curr;
if(i%3==0){
curr+= "Fizz";
}
if(i%5==0){
curr+="Buzz";
}
if(curr.size()==0){
curr+=to_string(i);
}
answer.emplace_back(curr);
}
【3】498. 对角线遍历
日期:10.8
2.类型: 矩阵,模拟
3.方法一:模拟(半解)
设矩阵的行数为 m, 矩阵的列数为 n:
设对角线从上到下的编号为 i∈[0,m+n−2]:
当 i 为偶数时,则第 i 条对角线的走向是从下往上遍历;
当 i 为奇数时,则第 i 条对角线的走向是从上往下遍历;
当第 i 条对角线从下往上遍历时,每次行索引减 1,列索引加 1,直到矩阵的边缘为止:
当 i<m 时,则此时对角线遍历的起点位置为 (i,0);
当 i≥m 时,则此时对角线遍历的起点位置为 (m−1,i−m+1);
当第 i 条对角线从上往下遍历时,每次行索引加 1,列索引减 1,直到矩阵的边缘为止:
当 i<n 时,则此时对角线遍历的起点位置为 (0,i);
当 i≥n 时,则此时对角线遍历的起点位置为 (i−n+1,n−1);
关键代码:
cpp
// 遍历所有对角线,共 m+n-1 条
for(int i=0;i<m+n-1;i++){
if(i%2){
int x=i<n?0:i-n+1;
int y=i<n?i:n-1;
while(x<m&&y>=0){
res.emplace_back(mat[x][y]);
x++;
y--;
}
}else{ // 偶数索引对角线:从左下向右上遍历
int x=i<m?i:m-1;
int y=i<m?0:i-m+1;
// 沿对角线遍历
while(x>=0&&y<n){
res.emplace_back(mat[x][y]);
x--;
y++;
}
}
}
【4】537. 复数乘法
日期:10.9
2.类型: 数学,字符串,模拟
3.方法一:模拟(官方题解)

正则表达式:"\\+|i"
\\+
:匹配加号字符 +
(需要转义)
|
:或操作符
i
:匹配虚数单位 i
sregex_token_iterator 工作原理
sregex_token_iterator
是C++的正则表达式令牌迭代器:
第一个参数:字符串起始迭代器
第二个参数:字符串结束迭代器
第三个参数:正则表达式模式
第四个参数:-1 表示返回不匹配的部分(即分割后的令牌)
关键代码:
cpp
// 定义正则表达式,匹配 '+' 或 'i'
regex re("\\+|i");
// 使用正则表达式分割第一个复数字符串
vector<string> complex1(sregex_token_iterator(num1.begin(),num1.end(),re,-1),
std::sregex_token_iterator());
// 使用正则表达式分割第二个复数字符串
vector<string> complex2(sregex_token_iterator(num2.begin(),num2.end(),re,-1),
std::sregex_token_iterator());
// 提取实部和虚部并转换为整数
int real1=stoi(complex1[0]);
int imag1=stoi(complex1[1]);
int real2=stoi(complex2[0]);
int imag2=stoi(complex2[1]);
// 应用复数乘法公式并返回结果字符串
return to_string(real1*real2-imag1*imag2)+"+"+
to_string(real1*imag2+imag1*real2)+"i";
【5】415. 字符串相加
日期:10.10
2.类型: 数学,字符串,模拟
3.方法一:模拟(半解)
定义两个指针 i 和 j 分别指向 num1 和 num2的末尾,即最低位,同时定义一个变量 add 维护当前是否有进位,然后从末尾到开头逐位相加即可。统一在指针当前下标处于负数的时候返回 0,等价于对位数较短的数字进行了补零操作,这样就可以除去两个数字位数不同情况的处理。
关键代码:
cpp
while(i>=0||j>=0||add!=0){
// 获取当前位的数字,如果已超出范围则用0填充
int x=i>=0?num1[i]-'0':0;
int y=j>=0?num2[j]-'0':0;
int result=x+y+add;
// 将当前位的结果添加到答案中
ans.push_back('0'+result%10);
add=result/10;
i-=1;
j-=1;
}
【6】566. 重塑矩阵
日期:10.11
2.类型: 数组,模拟
3.方法一:二维数组的一维表示(一次题解)
使用数学映射将原矩阵的线性索引转换为新矩阵的二维坐标
通过除法运算确定行索引,取模运算确定列索引
保持元素的相对顺序不变
关键代码:
cpp
if(m*n!=r*c){
return nums;
}
vector<vector<int>> ans(r, vector<int>(c));
for(int x=0;x<m*n;++x){
// 将原矩阵中的元素映射到新矩阵中
ans[x/c][x%c]=nums[x/n][x%n];
}
【7】592. 分数加减运算
日期:10.12
2.类型: 数学,字符串,模拟
3.方法一:模拟(半解)
逐个解析表达式中的分数
使用通分法进行分数加法:a/b + c/d = (a*d + c*b) / (b*d)
最后化简分数,求分子分母的最大公约数
cpp
while(index<n){
long long x1=0,sign=1;
if(expression[index]=='-'||expression[index]=='+'){
sign=expression[index]=='-' ? -1 : 1;
index++;
}
while(index<n&&isdigit(expression[index])){
x1=x1*10+expression[index]-'0';
index++;
}
x1=sign*x1;
index++;
long long y1=0;
while(index<n&&isdigit(expression[index])){
y1=y1*10+expression[index]-'0';
index++;
}
// 分数加法: x/y + x1/y1 = (x*y1 + x1*y) / (y*y1)
x=x*y1+x1*y;
y*= y1;
}
日期:10.13
2.类型:字符串,模拟
3.方法一:模拟(一次题解)
起始点x=0,y=0。接下来我们遍历指令并更新机器人的坐标:
如果指令是 U,则令 y=y−1
如果指令是 D,则令 y=y+1
如果指令是 L,则令 x=x−1
如果指令是 R,则令 x=x+1
最后判断 (x,y) 是否为 (0,0) 即可。
关键代码:
cpp
for(const auto& move: moves){
if(move=='U'){
y--;
}
else if(move=='D'){
y++;
}
else if(move=='L'){
x--;
}
else if(move=='R'){
x++;
}
}
【9】874. 模拟行走机器人
日期:10.14
2.类型:数组,哈希表,模拟
3.方法一:哈希表(半解)
使用方向数组表示四个基本方向
用哈希集合高效存储和检查障碍物
模拟机器人的每一步移动,遇到障碍物时停止
在移动过程中持续更新最大距离
关键代码:
cpp
// 将障碍物坐标转换为唯一整数并存入哈希集合
for(auto &obstacle:obstacles){
mp.emplace(obstacle[0]*60001+obstacle[1]);
}
int res=0;
for(int c : commands){
if(c<0){
d+=c==-1?1:-1; // -1右转90度,-2左转90度
d%=4; // 取模确保方向在0-3范围内
if(d<0){
d+=4; // 处理负数情况
}
}else{
// 移动命令
for(int i=0;i<c;i++){
// 检查下一个位置是否有障碍物
if(mp.count((px + dirs[d][0])*60001+py+dirs[d][1])){
break; // 遇到障碍物,停止移动
}
px+=dirs[d][0];
py+=dirs[d][1];
// 更新最大距离平方
res=max(res,px*px+py*py);
}
}
}
【10】495. 提莫攻击
日期:10.15
2.类型:数组,模拟
3.方法一:单词扫描(一次题解)
维护一个变量 expired
表示当前中毒效果的结束时间
对于每个攻击时间点:
如果攻击时没有中毒,添加完整的中毒时间
如果攻击时已经中毒,只添加新增加的中毒时间
累加所有有效的中毒时间
关键代码:
cpp
for(int i=0;i<timeSeries.size();++i){
if(timeSeries[i]>=expired){
// 攻击时没有中毒效果,添加完整的中毒时间
ans+=duration;
}else{
// 攻击时已有中毒效果,添加重叠部分的时间
ans+=timeSeries[i]+duration-expired;
}
// 更新中毒结束时间
expired=timeSeries[i]+duration;
}
【11】832. 翻转图像
日期:10.15
2.类型:数组,双指针,模拟
3.方法一:模拟优化 + 双指针(半解)
使用双指针技术同时完成水平翻转和像素反转
对于对称位置的两个像素:
如果它们相同,同时取反
如果它们不同,不需要操作(因为翻转后取反会相互抵消)
单独处理中间元素(当行长度为奇数时)
关键代码:
cpp
for(int i=0;i<n;i++){
int left=0,right=n-1;
while(left<right){
// 如果对称位置的像素值相同,同时取反
if(image[i][left]==image[i][right]){
image[i][left]^=1; // 使用异或操作取反
image[i][right]^=1; // 0^1=1, 1^1=0
}
left++;
right--;
}
// 处理中间元素(当矩阵大小为奇数时)
if(left==right){
image[i][left]^=1; // 中间元素取反
}
}
【12】1041. 困于环中的机器人
日期:10.15
2.类型:字符串,模拟
3.方法一:模拟(半解)
如果它的位置仍位于原点,那么不管它此时方向是什么,机器人都将永远无法离开。
如果它的位置不在原点,那么需要考虑此时机器人的方向:
如果机器人仍然朝北,那么机器人可以不会陷入循环。假设执行完一串指令后,机器人的位置是 (x,y) 且不为原点,方向仍然朝北,那么执行完第二串指令后,机器人的位置便成为 (2×x,2×y),会不停地往外部移动,不会陷入循环。
如果机器人朝南,那么执行第二串指令时,机器人的位移会与第一次相反,即第二次的位移是 (−x,−y),并且结束后会回到原来的方向。每两串指令之后,机器人都会回到原点,并且方向朝北,机器人会陷入循环。
如果机器人朝东,即右转了 90°。每执行一串指令,机器人都会右转 90°。那么第一次和第三次指令的方向是相反的,第二次和第四次指令的方向是相反的,位移之和也为 0,每四次指令之后,机器人都会回到原点,并且方向朝北,机器人会陷入循环。如果机器人朝西,也是一样的结果。
关键代码:
cpp
for(char instruction:instructions){
if(instruction=='G'){
// 前进:根据当前方向移动
x+=direc[direcIndex][0];
y+=direc[direcIndex][1];
}else if(instruction=='L'){
// 左转:逆时针旋转90度
direcIndex+=3; // 相当于 direcIndex - 1
direcIndex%=4; // 取模确保在0-3范围内
}else{
// 右转:顺时针旋转90度
direcIndex++;
direcIndex%=4; // 取模确保在0-3范围内
}
}