题解:洛谷 P1055 ISBN号码

【题目来源】

洛谷:P1055 [NOIP 2008 普及组] ISBN 号码 - 洛谷(luogu.com.cn)

【题目描述】

每一本正式出版的图书都有一个 ISBN 号码与之对应,ISBN 码包括 \(9\) 位数字、\(1\) 位识别码和 \(3\) 位分隔符,其规定格式如 x-xxx-xxxxx-x,其中符号 - 就是分隔符(键盘上的减号),最后一位是识别码,例如 0-670-82162-4 就是一个标准的 ISBN 码。ISBN 码的首位数字表示书籍的出版语言,例如 \(0\) 代表英语;第一个分隔符 - 之后的三位数字代表出版社,例如 \(670\) 代表维京出版社;第二个分隔符后的五位数字代表该书在该出版社的编号;最后一位为识别码。

识别码的计算方法如下:

首位数字乘以 \(1\) 加上次位数字乘以 \(2\) ……以此类推,用所得的结果  \(mod\ 11\),所得的余数即为识别码,如果余数为 \(10\),则识别码为大写字母 \(X\)。例如 ISBN 号码 0-670-82162-4 中的识别码 \(4\) 是这样得到的:对 067082162\(9\) 个数字,从左至右,分别乘以 \(1,2,\dots,9\) 再求和,即 \(0\times 1+6\times 2+\dots +2\times 9=158\),然后取 \(158\ mod\ 11\) 的结果 \(4\) 作为识别码。

你的任务是编写程序判断输入的 ISBN 号码中识别码是否正确,如果正确,则仅输出 Right;如果错误,则输出你认为是正确的 ISBN 号码。

【输入】

一个字符序列,表示一本书的 ISBN 号码(保证输入符合 ISBN 号码的格式要求)。

【输出】

一行,假如输入的 ISBN 号码的识别码正确,那么输出 Right,否则,按照规定的格式,输出正确的 ISBN 号码(包括分隔符 -)。

【输入样例】

0-670-82162-4

【输出样例】

Right

【核心思想】

  1. 问题分析:给定一个 ISBN 号码字符串(格式为 x-xxx-xxxxx-x),需要验证最后一位识别码是否正确。识别码计算规则:取前 9 位数字(忽略分隔符 -),从左到右分别乘以 \(1 \sim 9\) 后求和,对 \(11\) 取模,余数为 \(10\) 时识别码为 X,否则为余数对应的数字。

  2. 算法选择

    • 字符串遍历 + 加权求和:遍历字符串前部(除最后一位识别码),跳过 -,对数字字符按权重累加
    • 模运算求识别码:加权和对 \(11\) 取模,根据余数确定正确识别码
    • 验证与修正:比较计算出的识别码与输入的最后一位,正确输出 Right,错误则替换后输出完整 ISBN
  3. 关键步骤

    • 读入数据cin >> s 读取 ISBN 字符串
    • 计算加权和
      • 遍历 \(i\)\(0\)s.size() - 2(除最后一位识别码)
      • s[i] != '-':数字字符转数值 s[i] - '0',权重 ++cnt(从 1 递增到 9),累加 sum += num * cnt
    • 计算正确识别码
      • m = sum % 11
      • m < 10c = m + '0'(数字字符)
      • 否则:c = 'X'
    • 验证输出
      • s[s.size() - 1] == c:输出 Right
      • 否则:s[s.size() - 1] = c,输出修正后的字符串
  4. 时间/空间复杂度

    • 时间复杂度:\(O(|s|)\),线性遍历字符串
    • 空间复杂度:\(O(1)\),仅需常数个变量
  5. 校验码验证的核心思想

    • 权重递增:第 \(i\) 个有效数字的权重为 \(i\)(从 1 开始),体现位置敏感性
    • 模 11 设计:选择 11 作为模数是因为它是大于 10 的最小质数,能检测大多数常见错误(如相邻数字交换、单个数字错误)
    • X 特殊处理:余数为 10 时用 X 表示,因识别码只能是单个字符
    • 原位修正:错误时直接修改字符串最后一位后输出,无需重建字符串
    • 适用于校验码计算、字符串处理、模运算验证类问题

【算法标签】

普及- #字符串入门

【代码详解】

#include <bits/stdc++.h>  // 包含标准库头文件
using namespace std;

int main()
{
    string s;  // 存储输入的ISBN字符串
    cin >> s;  // 读取输入

    int sum = 0;  // 用于计算加权和
    int cnt = 0;  // 当前数字的权重计数器

    // 计算前9位数字的加权和
    for (int i = 0; i < s.size() - 1; i++) {
        if (s[i] != '-') {  // 跳过分隔符'-'
            sum += (s[i] - '0') * ++cnt;  // 数字字符转数值并计算加权和
        }
    }

    // 计算校验码
    int m = sum % 11;  // 求余数
    char c;  // 存储计算出的校验码
    if (m < 10) {
        c = m + '0';  // 余数0-9转为字符
    } else {
        c = 'X';  // 余数10对应'X'
    }

    // 验证校验码
    if (s[s.size() - 1] == c) {  // 校验码正确
        cout << "Right";
    } else {  // 校验码错误
        s[s.size() - 1] = c;  // 修正校验码
        cout << s;  // 输出修正后的ISBN
    }

    return 0;
}

【运行结果】

0-670-82162-0
0-670-82162-4
posted @ 2026-08-05 15:44  团爸讲算法  阅读(18)  评论(0)    收藏  举报