题解:洛谷 P1553 数字反转(升级版)
【题目来源】
【题目描述】
给定一个数,请将该数各个位上数字反转得到一个新数。
这次与 NOIP2011 普及组第一题不同的是:这个数可以是小数,分数,百分数,整数。
- 整数反转是将所有数位对调。
- 小数反转是把整数部分的数反转,再将小数部分的数反转,不交换整数部分与小数部分。
- 分数反转是把分母的数反转,再把分子的数反转,不交换分子与分母。
- 百分数的分子一定是整数,百分数只改变数字部分。
【输入】
一个实数 \(s\)
【输出】
一个实数,即 \(s\) 的反转数
【输入样例】
700/27
【输出样例】
7/72
【核心思想】
-
问题分析:给定一个数,可能是整数、小数、分数或百分数,要求将数字部分(整数部分、分子、分母等)按位反转,并保持原格式(如小数点、分数线、百分号的位置不变)。整数反转需去掉前导零(反转后可能产生前导零,需去除);小数反转需分别反转整数部分和小数部分,并处理小数部分末尾的零;分数反转需分别反转分子和分母;百分数只反转数字部分。本质是一个字符串解析与分段反转问题。
-
算法选择:
- 分段处理:根据输入字符串的特征(包含
.、/、%或纯数字)判断类型,然后分割成对应的部分,分别调用统一的“反转并去除前导零”函数处理,最后按原格式拼接输出。 - 反转函数:设计
f0(string s),对数字字符串进行反转,并从最高位开始去除前导零(若全为零则返回"0")。
- 分段处理:根据输入字符串的特征(包含
-
关键步骤:
- 读入:读取字符串
s。 - 分类处理:
- 整数:直接调用
f0(s)。 - 小数:按
.分割为整数部分s1和小数部分s2。整数部分调用f0,小数部分逆序反转并去除末尾的零(即反转后前面的零),然后拼接。 - 分数:按
/分割为分子s1和分母s2,分别调用f0,中间用/连接。 - 百分数:按
%分割,数字部分调用f0,末尾加%。
- 整数:直接调用
- 输出:输出处理后的字符串。
- 读入:读取字符串
-
时间/空间复杂度:
- 时间复杂度:\(O(n)\),每个字符处理常数次。
- 空间复杂度:\(O(n)\),存储分割后的子串。
-
字符串分割与反转:
- 统一反转函数:
f0负责反转字符串并去除前导零(对于整数部分和分子分母),这是代码复用的关键。 - 小数部分的特殊处理:小数部分反转时,需要保留有效数字,即去除反转后末尾的零(对应原数前面的零)。代码中通过找到第一个非零位置
mark,然后从末尾到mark输出。 - 格式保留:处理过程中不改变分隔符(
.、/、%)的位置和数量。 - 边界情况:如
0、0.0、0%等,需确保输出0而非空字符串。 - 适用场景:这类“数字反转升级版”是字符串处理的综合题,考察分割、反转、去零等操作。
- 统一反转函数:
【算法标签】
普及- #字符串入门
【代码详解】
#include <bits/stdc++.h> // 包含标准库头文件(万能头文件)
using namespace std; // 使用标准命名空间
/**
* 翻转整数部分
* @param s 要翻转的数字字符串
*/
void f0(string s)
{
int len = s.length(); // 获取字符串长度
int mark; // 标记第一个非零字符的位置
// 从后往前找到第一个非零字符
for (mark = len - 1; mark >= 0; mark--)
{
if (s[mark] != '0')
break;
}
// 如果全是零,输出0并返回
if (mark < 0)
{
cout << 0;
return;
}
// 从第一个非零字符开始倒序输出
for (int i = mark; i >= 0; i--)
{
cout << s[i];
}
}
/**
* 翻转小数部分
* @param s 要翻转的小数字符串
*/
void f1(string s)
{
int m = s.find("."); // 找到小数点位置
int len = s.length(); // 获取字符串长度
string s1 = s.substr(0, m); // 提取整数部分
string s2 = s.substr(m + 1, len - m - 1); // 提取小数部分
f0(s1); // 翻转整数部分
cout << '.'; // 输出小数点
// 处理小数部分前导零
int mark = 0;
for (mark = 0; mark < s2.length(); mark++)
{
if (s2[mark] != '0')
break;
}
// 如果小数部分全是零,输出0并返回
if (mark >= s2.length())
{
cout << 0;
return;
}
// 翻转小数部分(去掉末尾的零)
for (int i = s2.length() - 1; i >= mark; i--)
{
cout << s2[i];
}
}
/**
* 翻转分数部分
* @param s 要翻转的分数字符串
*/
void f2(string s)
{
int m = s.find("/"); // 找到分数线位置
int len = s.length(); // 获取字符串长度
string s1 = s.substr(0, m); // 提取分子部分
string s2 = s.substr(m + 1, len - m - 1); // 提取分母部分
f0(s1); // 翻转分子部分
cout << '/'; // 输出分数线
f0(s2); // 翻转分母部分
}
/**
* 翻转百分数部分
* @param s 要翻转的百分数字符串
*/
void f3(string s)
{
int m = s.find("%"); // 找到百分号位置
int len = s.length(); // 获取字符串长度
string s1 = s.substr(0, m); // 提取数字部分
f0(s1); // 翻转数字部分
cout << '%'; // 输出百分号
}
int main()
{
string s; // 定义字符串:存储输入的数字
cin >> s; // 输入数字字符串
// 根据数字类型调用对应的处理函数
if (s.find("/") != -1) // 如果是分数
{
f2(s);
}
else if (s.find("%") != -1) // 如果是百分数
{
f3(s);
}
else if (s.find(".") != -1) // 如果是小数
{
f1(s);
}
else // 如果是整数
{
f0(s);
}
return 0; // 程序正常结束
}
【运行结果】
700/27
7/72
浙公网安备 33010602011771号