LeetCode 13 罗马数字转整数

1. 题目

13. 罗马数字转整数 - 力扣(LeetCode)

题目描述

罗马数字包含以下七种字符:IVXLCDM

字符 数值
I 1
V 5
X 10
L 50
C 100
D 500
M 1000

特殊规则:

  1. 正常情况大数在右,直接相加,如 III=3VI=6

  2. 减法特例:IV/X 前 =4/9;XL/C 前 = 40/90;CD/M 前 = 400/900。

给定合法罗马字符串,转换为对应整数。

示例

输入:III → 3

输入:IV → 4

输入:IX → 9

输入:LVIII → 58

输入:MCMXCIV → 1994

约束

  • 1≤s .length≤15

  • s 仅由 I,V,X,L,C,D,M 组成,输入保证合法罗马数字

2. 最佳解题思路描述(哈希映射 + 后项比较,极简通用)

核心规律

罗马数字整体从左到右数值递减;

  • 若当前字符值 < 右侧字符值:属于减法组合,总和减去当前值;

  • 其余情况:总和加上当前值。

步骤:

  1. 建立字符到数字的映射表;

  2. 遍历字符串到倒数第二位:

    • map[s[i]] < map[s[i+1]]sum -= map[s[i]]

    • 否则:sum += map[s[i]]

  3. 最后单独加上末尾字符的值;

优势

  • 代码短,无冗长 switch 分支;

  • 统一一套判断逻辑,不用分七种字符单独处理特殊情况;

  • 时间 O (n ),空间 O(1)(固定 7 个映射)。

3. 我的可优化代码(逻辑存在多处 bug,思路繁琐)

复制代码
class Solution {
public:
    int romanToInt(string s) {
        int sum = 0;
        for(int i=0;i<s.length();i++){
            switch(s[i]){
                case 'I':
                    if(s[i+1]=='V'||s[i+1]=='X')
                        break;
                    sum++;
                    break;
                case 'V':
                    if(i>0 && s[i-1]=='I'){
                        sum+=4;
                        break;
                    }
                    sum+=5;
                    break;
                case 'X':
                    if(i>0 && s[i-1]=='I'){
                        sum+=9;
                        break;
                    }
                    if(s[i+1]=='L'||s[i+1]=='C')
                        break;
                    sum+=10;
                    break;
                case 'L':
                    if(i>0 && s[i-1]=='X'){
                        sum+=40;
                        break;
                    }
                    sum+=50;
                    break;
                case 'C':
                    if(i>0 && s[i-1]=='X'){
                        sum+=90;
                        break;
                    }
                    if(s[i+1]=='D'||s[i+1]=='M')
                        break;
                    sum+=100;
                    break;
                case 'D':
                    if(i>0 && s[i-1]=='C'){
                        sum+=400;
                        break;
                    }
                    sum+=500;
                    break;
                case 'M':
                    if(i>0 && s[i-1]=='C'){
                        sum+=900;
                        break;
                    }
                    sum+=1000;
                    break;
                default:
                    break;
            }
        }
        return sum;
    }
};

代码致命 bug

  1. 越界访问 s[i+1]

    i 走到最后一位时i+1超出字符串下标,访问非法内存,运行崩溃;

  2. 重复叠加数值

    例如IV:i=0 (I) 满足si+1=='V'直接 break,不加 1;i=1 (V) 判断前一位是 I,sum +=4,结果正确;

    但IX、XL、XC、CD、CM均会出现重复特殊值叠加逻辑,极容易算错;

  3. 分支逻辑割裂,极易漏写 / 写错特殊条件

    七种字符分开处理,每个字符单独判断左右相邻,代码冗余庞大,维护困难;

  4. 错误判断:X 分支判断 s[i-1]=='I' 求 9,逻辑写反,IX 是 I 在前 X 在后,该判断永远不会触发,IX 计算直接出错。

整体缺陷

  • 靠 switch 暴力分情况,代码冗长、边界越界、逻辑易出错;

  • 没有统一的数学判断规则,靠人工枚举所有减法组合,扩展性差。

4. 最优标准代码(哈希映射,统一判断)

复制代码
#include <unordered_map>
#include <string>
using namespace std;
class Solution {
public:
    int romanToInt(string s) {
        unordered_map<char, int> mp = {
            {'I',1},{'V',5},{'X',10},{'L',50},
            {'C',100},{'D',500},{'M',1000}
        };
        int sum = 0;
        int n = s.size();
        for(int i = 0; i < n - 1; i++){
            if(mp[s[i]] < mp[s[i+1]]){
                sum -= mp[s[i]];
            }else{
                sum += mp[s[i]];
            }
        }
        // 最后一位一定只加不减
        sum += mp[s.back()];
        return sum;
    }
};

5. 总结

  1. 你的 switch 暴力分支写法存在数组越界、逻辑判断错误,无法正常通过用例,不推荐;

  2. 通用核心规则:前小后大则减当前值,否则加当前值,一套逻辑覆盖所有情况;

  3. 遍历只到倒数第二位,避免访问 i+1 越界,末尾字符单独累加;

  4. 用哈希表存储字符数值映射,消除大量重复 if/switch 分支,代码简洁易读。

6. 相关知识拓展

拓展 1:反向遍历简化写法

从后往前遍历,记录最大值,当前值小于最大值则相减,否则更新最大值并相加:

复制代码
int romanToInt(string s) {
    unordered_map<char,int> mp={{'I',1},{'V',5},{'X',10},{'L',50},{'C',100},{'D',500},{'M',1000}};
    int sum=0,maxVal=0;
    for(int i=s.size()-1;i>=0;i--){
        int cur=mp[s[i]];
        if(cur<maxVal) sum-=cur;
        else {
            sum+=cur;
            maxVal=cur;
        }
    }
    return sum;
}

拓展 2:进阶题目 LC12 整数转罗马数字

逆向转换,采用贪心,从大到小匹配数值符号对;

拓展 3:复杂度对比

  • switch 暴力分支:代码冗余,存在越界 bug,理论时间 O (n);

  • 哈希正向遍历最优解:时间 O (n),空间 O (1)(固定 7 组映射);

  • 反向遍历:时间 O (n),空间 O (1)。

拓展 4:易错点记忆

  1. 正向遍历不要访问 s[n],循环上限 n-1

  2. 减法组合本质:小数出现在大数左侧,统一做减法,无需单独枚举 IV、IX 等所有特例。

相关推荐
m沐沐1 小时前
【计算机视觉】OpenCV 物体跟踪——原理、算法与CSRT跟踪器实战
人工智能·python·深度学习·opencv·算法·计算机视觉·人脸识别
START_GAME2 小时前
yolo模型笔记
笔记·yolo
怪奇云呼军2 小时前
闪电智能 Voice Agent 怎样根据语速、停顿和追问方式切换话术?策略引擎拆解
开发语言·人工智能·网络协议·算法·语音识别·web app
稚南城才子,乌衣巷风流2 小时前
树链剖分(树剖)算法详解:从原理到实现
算法·深度优先
jz_ddk2 小时前
[信号处理] 数学与工程之美:相邻差分共轭乘积
数学·算法·信号处理·差分·共轭
晓梦林2 小时前
[极客大挑战 2019]Secret File学习笔记
笔记·学习
摇滚侠9 小时前
AI 编程工具 《TRAE 官方手册》阅读笔记 AI 编程核心 上
人工智能·笔记
m沐沐11 小时前
【深度学习】YOLOv2目标检测算法——改进点、网络结构与聚类先验框解析
人工智能·pytorch·深度学习·算法·yolo·目标检测·transformer
不会就选b11 小时前
算法日常・每日刷题--<链表>3
数据结构·算法·链表