[数组][简单]13.罗马数字转整数

本文详细解析了将罗马数字转换为整数的算法,包括常规解法和使用哈希表的优化方案,通过具体示例阐述了特殊规则的处理方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


tags:

  • 简单
  • 完成
  • 数组
  • 哈希表
    flag: green
    style: summer
    date: ‘2019-6-5’

13.罗马数字转整数

一 、题目

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

字符数值
I1
V5
X10
L50
C100
D500
M1000

例如, 罗马数字 2 写做 II ,即为两个并列的 1。12 写做 XII ,即为 X + II 。 27 写做 XXVII, 即为 XX + V + II

通常情况下,罗马数字中小的数字在大的数字的右边。但也存在特例,例如 4 不写做 IIII,而是 IV。数字 1 在数字 5 的左边,所表示的数等于大数 5 减小数 1 得到的数值 4 。同样地,数字 9 表示为 IX。这个特殊的规则只适用于以下六种情况:

  • I 可以放在 V (5) 和 X (10) 的左边,来表示 4 和 9。
  • X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。
  • C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。

给定一个罗马数字,将其转换成整数。输入确保在 1 到 3999 的范围内。

示例 1:

输入: “III”
输出: 3

示例 2:

输入: “IV”
输出: 4

示例 3:

输入: “IX”
输出: 9

示例 4:

输入: “LVIII”
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:

输入: “MCMXCIV”
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

二、解法

  • 常规解法:
    第一次遍历一个字符把符合值加到结果里,第二次遍历两个字符从结果中减去符合值。
class Solution {
    public int romanToInt(String s) {
	int result = 0;
        for(int i = 0; i < s.length(); i++) {
            switch(s.charAt(i)) {
                case 'I': 
                    result += 1;
                    break;
                case 'V': 
                    result += 5;
                    break;
                case 'X':
                    result += 10;
                    break;
                case 'L':
                    result += 50;
                    break;
                case 'C':
                    result += 100;
                    break;
                case 'D':
                    result += 500;
                    break;
                case 'M':
                    result += 1000;
                    break;
            }
        }
	for(int j = 0; j < s.length() - 1; j++) {
            switch("" + s.charAt(j) + s.charAt(j+1)) {
                case "IV":
                case "IX":
                    result -= 2;
                    break;
                case "XL":
                case "XC":
                    result -= 20;
                    break;
                case "CD":
                case "CM":
                    result -= 200;
                    break;
            }
        }
        return result;
    }
}

  • 哈希表解法:

最基础情况
试想一下,假如没有那几条特殊的规则,就是最简单的文字转换。那像XIV这样就可以直接转换为10+1+5=16

注意:这是我们的整体思路,即字符的转换。

特殊情况
现在我们得知有三大种、六小种特殊情况,即:

I(1) 和 V(5) 或 X(10) 组成 IV(4) 和 IX(9)

X(10) 和 L(50) 或 C(100) 组成 XL(40) 和 XC(90)

C(100) 和 D(500) 或 M(1000) 组成 CD(400) 和 CM(900)

我把他们写成这样的形式相信大家一眼就可以看出来这其中的规律:当右边的数是左边的数5倍或10倍的时候,左边的数就去相反数与其相加即可。

举个例子,IV其中V(5)是I(1)的五倍,那么这时候两者在一起,只需将I取值为相反数-1,然后再相加即可。那么XIV也就可以写成10±1+5=14(可以看看后记哦)

需要注意的地方
因为需要用到前一个和后一个值做比较,所以需要考虑下标是否越界的问题,因此在遍历字符串时要在倒数第二个地方结束,即i只能取值0 <= i < s.length()-1。最后再加上字符串最后一位的值。

class Solution {
    public int romanToInt(String s) {
        HashMap<Character,Integer> map=new HashMap<Character,Integer>();//使用map把他们对应存储起来
        map.put('I',1);
        map.put('V',5);
        map.put('X',10);
        map.put('L',50);
        map.put('C',100);
        map.put('D',500);
        map.put('M',1000);
        
        int res=0;          //存放结果
        int current=0;     //第一个值
        int next=0;       //后一个值
        
        for(int i=0;i<s.length()-1;++i){        //注意这里的s.length()-1是为了防止下标越界
            current=map.get(s.charAt(i));
            next=map.get(s.charAt(i+1));

            if(next/current==5||next/current==10){    //如果满足后一个是前一个5倍或10倍
                res-=map.get(s.charAt(i));            //结果减掉当前值(相当于加了相反数)
            }else{
                res+=map.get(s.charAt(i));            //否则加上当前值
            }
        }
        
        res+=map.get(s.charAt(s.length()-1));        //最后再加上字符串末尾的值
        
        return res;
    }
}

后记
这里能有人会问,那万一是 V(5) 和 L(50) 放在一起呢?这也是十倍的关系啊!说实话一开始我也没有想到这一点,就是在写这篇文章的时候才发现的。但事实上这是不符合题目假设的,因为题目说明除了6种情况外,小的数必须在大的数右边。

但本着杠精的思想,我提交了测试,发现LeetCode返回的结果是VL=45,这让我很惊讶!于是我大胆尝试测试了一个V(5)和C(100)组合是什么结果,结果更让我惊讶的是VC=95,但按照我之前写的思路,应该是VC=105。这样看来我萌生了一个大胆的想法,只要是左边的数比右边的数小,那么左边的数就要变成相反数,基于这一想法我修改代码,再次提交。再次惊讶,竟然运行时间比之前减小了20%。咱也不知道为啥,咱也不敢问。多半是判断条件减少了所以快了吧。不知道这种小数在大数左边的到底该怎么判定,不该返回0或者错误吗??

这是修改后的代码(就改了个判断条件)



class Solution {
    public int romanToInt(String s) {
        HashMap<Character,Integer> map=new HashMap<Character,Integer>();
        map.put('I',1);
        map.put('V',5);
        map.put('X',10);
        map.put('L',50);
        map.put('C',100);
        map.put('D',500);
        map.put('M',1000);
        
        int res=0;
        int current=0;
        int next=0;
        
        for(int i=0;i<s.length()-1;++i){
            current=map.get(s.charAt(i));
            next=map.get(s.charAt(i+1));
            if(next>current){
                res-=map.get(s.charAt(i));
            }else{
                res+=map.get(s.charAt(i));
            }
        }
        
        res+=map.get(s.charAt(s.length()-1));
        
        return res;
    }
}

欢迎大家讨论这个VC的问题
作者:godk-1
链接:https://leetcode-cn.com/problems/two-sum/solution/zhua-zhu-guan-jian-qu-bie-xiang-xi-fen-xi-ti-mu-by/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值