题解:洛谷 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
【核心思想】
-
问题分析:给定一个 ISBN 号码字符串(格式为
x-xxx-xxxxx-x),需要验证最后一位识别码是否正确。识别码计算规则:取前 9 位数字(忽略分隔符-),从左到右分别乘以 \(1 \sim 9\) 后求和,对 \(11\) 取模,余数为 \(10\) 时识别码为X,否则为余数对应的数字。 -
算法选择:
- 字符串遍历 + 加权求和:遍历字符串前部(除最后一位识别码),跳过
-,对数字字符按权重累加 - 模运算求识别码:加权和对 \(11\) 取模,根据余数确定正确识别码
- 验证与修正:比较计算出的识别码与输入的最后一位,正确输出
Right,错误则替换后输出完整 ISBN
- 字符串遍历 + 加权求和:遍历字符串前部(除最后一位识别码),跳过
-
关键步骤:
- 读入数据:
cin >> s读取 ISBN 字符串 - 计算加权和:
- 遍历 \(i\) 从 \(0\) 到
s.size() - 2(除最后一位识别码) - 若
s[i] != '-':数字字符转数值s[i] - '0',权重++cnt(从 1 递增到 9),累加sum += num * cnt
- 遍历 \(i\) 从 \(0\) 到
- 计算正确识别码:
m = sum % 11- 若
m < 10:c = m + '0'(数字字符) - 否则:
c = 'X'
- 验证输出:
- 若
s[s.size() - 1] == c:输出Right - 否则:
s[s.size() - 1] = c,输出修正后的字符串
- 若
- 读入数据:
-
时间/空间复杂度:
- 时间复杂度:\(O(|s|)\),线性遍历字符串
- 空间复杂度:\(O(1)\),仅需常数个变量
-
校验码验证的核心思想:
- 权重递增:第 \(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
浙公网安备 33010602011771号