数字反转(升级版)

按整数、小数、分数、百分数四种格式拆分字符串,分别反转数字部分并按规则去掉多余的零。

OJ: luogu

题目 ID: P1553

难度:普及-

标签:字符串模拟分类讨论python

日期: 2026-07-06 20:42

题意

给出一个数字字符串,它可能是整数、小数、分数或百分数。要求把每个数字部分反转,并按题目规则删除多余的 0

思路

先按特殊符号判断类型:

形式 分隔符 处理方式
整数 整体反转,再去掉前导零
小数 . 整数部分和小数部分分别反转
分数 / 分子和分母分别反转,不约分
百分数 % 只反转 % 前面的数字

去零规则要分清方向:

  • 整数部分反转后要去掉前导零,例如 700 -> 7
  • 小数部分反转后要去掉末尾零,例如 084 -> 480 -> 48
  • 如果删除后为空,要保留一个 0

这题是字符串分类讨论,历史目录中保留了 C++ 暴力文件,但本文以 Python 正解为准,不再创建 brute.py

Python 知识

  • /home/rainboy/mycode/hugo-blog/content/program_language/python/input_output_and_strings.md:字符串切片 s[::-1] 可以反转字符串。
  • /home/rainboy/mycode/hugo-blog/content/program_language/python/oj_input_output_cheatsheet.md:单个字符串输入直接用 input().strip()
  • split(".")split("/") 适合按分隔符拆成两部分。
  • lstrip("0") 删除左侧零,rstrip("0") 删除右侧零。

代码

python
def reverse_integer_part(text):
    result = text[::-1].lstrip("0")
    return result if result else "0"


def reverse_decimal_part(text):
    result = text[::-1].rstrip("0")
    return result if result else "0"


s = input().strip()

if "." in s:
    integer_part, decimal_part = s.split(".")
    print(reverse_integer_part(integer_part) + "." + reverse_decimal_part(decimal_part))
elif "/" in s:
    numerator, denominator = s.split("/")
    print(reverse_integer_part(numerator) + "/" + reverse_integer_part(denominator))
elif s.endswith("%"):
    print(reverse_integer_part(s[:-1]) + "%")
else:
    print(reverse_integer_part(s))

Guide 风格代码

cppbook《C++ 快速入门》教学风格的写法(std:: 前缀、i += 1 循环、0 起始下标):

cpp
/**
 * Author by Rainboy blog: https://rainboylv.com github: https://github.com/rainboylvx
 * rbook: -> https://rbook.roj.ac.cn  https://rbook2.roj.ac.cn
 * rainboy的学习导航网站: https://idx.roj.ac.cn
 * create_at: 2026-08-14 14:53
 * update_at: 2026-08-14 14:53
 */
#include <iostream>
#include <string>

// 反转"整数部分":从后往前拼出新串,再去掉新串开头多余的 0
std::string reverse_integer_part(std::string s) {
    std::string result = "";
    int length = (int)s.size();
    for (int i = length - 1; i >= 0; i -= 1) {
        result += s[i];
    }
    // 至少保留一位数字,所以 pos + 1 < size 时才继续跳过
    int pos = 0;
    while (pos + 1 < (int)result.size() && result[pos] == '0') {
        pos += 1;
    }
    return result.substr(pos);
}

// 反转"小数部分":反转后去掉末尾的 0,但至少要保留一个 '0'
std::string reverse_decimal_part(std::string s) {
    std::string result = "";
    int length = (int)s.size();
    for (int i = length - 1; i >= 0; i -= 1) {
        result += s[i];
    }
    while ((int)result.size() > 1 && result[result.size() - 1] == '0') {
        result.pop_back();
    }
    return result;
}

int main() {
    std::string s;
    std::cin >> s;

    std::size_t dot_pos = s.find('.');
    std::size_t slash_pos = s.find('/');
    std::size_t percent_pos = s.find('%');

    if (dot_pos != std::string::npos) {
        std::string left = s.substr(0, dot_pos);
        std::string right = s.substr(dot_pos + 1);
        std::cout << reverse_integer_part(left) << '.' << reverse_decimal_part(right) << '\n';
    } else if (slash_pos != std::string::npos) {
        // 分数:分子分母分别反转,不带符号,不交换位置
        std::string left = s.substr(0, slash_pos);
        std::string right = s.substr(slash_pos + 1);
        std::cout << reverse_integer_part(left) << '/' << reverse_integer_part(right) << '\n';
    } else if (percent_pos != std::string::npos) {
        // 百分数:只反转数字部分,百分号留在末尾
        std::string number = s.substr(0, percent_pos);
        std::cout << reverse_integer_part(number) << "%\n";
    } else {
        std::cout << reverse_integer_part(s) << '\n';
    }

    return 0;
}

Pythonic 写法

match 按小数/分数/百分号分支反转:

python
def rev_int(text: str) -> str:
    return text[::-1].lstrip("0") or "0"


def rev_dec(text: str) -> str:
    return text[::-1].rstrip("0") or "0"


s = input().strip()
match s:
    case _ if "." in s:
        left, right = s.split(".")
        print(f"{rev_int(left)}.{rev_dec(right)}")
    case _ if "/" in s:
        left, right = s.split("/")
        print(f"{rev_int(left)}/{rev_int(right)}")
    case _ if s.endswith("%"):
        print(f"{rev_int(s[:-1])}%")
    case _:
        print(rev_int(s))

复杂度

设输入长度为 n,每次反转和去零都是线性操作,时间复杂度是 O(n)O(n),空间复杂度是 O(n)O(n)

总结

本题最容易错的是去零方向。整数部分去前导零,小数部分去末尾零;分数和百分数仍然只处理各自的数字部分。