罗马数字合法字符为I、V、X、L、C、D、M,须满足减法规则(仅IV、IX、XL、XC、CD、CM),禁止重复相同字符(如VV)、小写或非法字符,需贪心扫描验证数值递减与减法逻辑。

罗马数字的合法字符和基本规则
有效的罗马数字只能由 I、V、X、L、C、D、M 组成,且必须满足减法规则(如 IV 表示 4,IX 表示 9),不能随意组合。比如 IIII 虽然语义上可理解为 4,但标准罗马数字写法是 IV;VV、LL、DD 等重复表示同一值的字符是非法的。
判断前先做快速筛查:
- 字符串为空或含非大写罗马字母字符 → 直接返回 false
- 检查每个字符是否在预定义集合中:
std::unordered_set<char>{'I','V','X','L','C','D','M'}</char> - 注意大小写:输入含小写字母(如
"iv")应视为无效,除非明确要求忽略大小写
如何用贪心扫描验证顺序和减法逻辑
从左到右遍历,用映射表把字符转为数值(map<char int></char> 或数组索引),关键在于处理「小数在大数左边」的减法情形(仅限 IV、IX、XL、XC、CD、CM):
- 若当前字符值
- 否则直接累加当前字符值
- 一旦遇到非法减法(如
IM、XM、VL)或越界访问 → 返回 false - 不建议边扫边构造整数再反向转回罗马数字比对——效率低且掩盖原始格式问题
示例片段逻辑:
立即学习“C++免费学习笔记(深入)”;
int val(char c) {
switch(c) {
case 'I': return 1;
case 'V': return 5;
case 'X': return 10;
case 'L': return 50;
case 'C': return 100;
case 'D': return 500;
case 'M': return 1000;
default: return -1;
}
}
// 扫描时:if (i+1 < s.size() && val(s[i]) < val(s[i+1])) { ... }
哪些常见输入会误判?怎么防
单纯按字符值递减/递增判断会漏掉结构性错误。例如:
-
"IIII":数值上等于 4,但不符合标准书写规范 → 应拒绝 -
"VIV":虽然每对局部满足规则,但整体无意义(5 + 4 = 9 的写法是IX)→ 需要限制重复次数:I、X、C最多连续 3 次;V、L、D不允许重复 -
"IC":跳过I直接到C是非法减法(只允许IV/IX、XL/XC、CD/CM)→ 必须显式枚举合法减法对 -
"MMMMD":超过 3999 的数虽有扩展记法,但标准罗马数字上限是 3999 → 若业务要求严格遵循传统,需额外校验总值 ≤ 3999
C++ 实现时容易忽略的边界细节
实际写函数时,几个技术点常被跳过:
- 空字符串或单字符需单独处理,避免
i+1越界访问 - 用
std::string::at()会抛异常,推荐用[]+ 手动长度检查 - 不要用
std::stoi或流操作解析——它们不是为罗马数字设计的 - 若需支持 Unicode 字符串(如宽字符
std::wstring),映射表必须用wchar_t键,且注意 Windows 下char默认是 ANSI - 性能敏感场景下,把字符映射改为 256 大小的查表数组(
int charToVal[256])比std::map或switch更快
最易错的是把「能算出一个整数」等同于「是有效罗马数字」——格式合法性永远优先于数值合理性。


















