day7_leetcode13

leetcode第13题:罗马数字转整数

1.题目:
罗马数字包含以下七种字符: I, V, X, L,C,D 和 M。

字符          数值
I             1
V             5
X             10
L             50
C             100
D             500
M             1000
例如, 罗马数字 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 的范围内。

跟上一次做的(也就是第11题整数转罗马数字)刚好相反

 

2.解法一:
思路,得到每个罗马数字对应的整数,然后加起来就是了,但是要注意一个问题,就是特殊的那几个罗马数字,它是两个罗马数字对应一个整数。
代码一:
 

class Solution:
    def romanToInt(self, s):
        roma_dict = {"I": 1, "IV": 4, "V": 5, "IX": 9, "X": 10, "XL": 40, "L": 50,
                     "XC": 90, "C": 100, "CD": 400, "D": 500, "CM": 900, "M": 1000}
        i = 0
        rst = 0
        while i <= len(s):
            if i == len(s):
                rst += roma_dict[s[i]]
                break
            if s[i] in roma_dict.keys() and s[i] + s[i+1] not in roma_dict.keys():
                rst += roma_dict[s[i]]
                i += 1
            else:
                rst += roma_dict[s[i]+s[i+1]]
                i += 2
        return rst

执行结果:
   

效果不太好。现在来说一下我的这个代码


(1)首先是定义一个变量roma_dict,然后把哪些可能出现的组合以字典的方式放在一起


(2)利用循环,从第一个字符开始判断,但是每次判断不仅要看当前循环到的字符,还要看下一个字符,因为这两个字符可能会组成特殊的那几个数字,如果能组成,那么就将这两个字符对应的整数在原有的基础上相加,然后i+2,这样就可以。如果当前字符和下一个字符得到的不是特殊的那几个,那么就只将当前的字符对应的整数在原有的基础上相加,最后i+1。


(3)但是要注意一个问题,就是如果最后一次循环时,i就是s的最后一个字符,那么执行s[i+1]就会出错,越界了,所以要先判断是不是到最后一个字符了,如果是,直接将其对应的整数加上就可以了,儿不必再判断之后的内容。


3.解法二:
结果肯定是不满意的,那么有什么优化的方法呢?我觉得循环那儿可以先不动,看一下能不能把不必要的条件判断语句删了。比如判断s[i]是否在roma_dict中,这个就是多余的,因为由题意,它给的罗马数字一定是合法的,所以s[i]当然在。其实我们只需要判断s[i+1]是否在就可以了,因为如果在,那说明是两个罗马数字对应一个整数,如果不是,那就是一个罗马数字对应一个整数。

代码二:

class Solution:
    def romanToInt(self, s):
        roma_dict = {"I": 1, "IV": 4, "V": 5, "IX": 9, "X": 10, "XL": 40, "L": 50,
                     "XC": 90, "C": 100, "CD": 400, "D": 500, "CM": 900, "M": 1000}
        i = 0
        rst = 0
        while i < len(s):
            try:
                if s[i] + s[i+1] in roma_dict:
                    rst += roma_dict.get(s[i]+s[i+1])
                    i += 2
                else:
                    rst += roma_dict.get(s[i])
                    i += 1
            except:
                rst += roma_dict.get(s[i])
                break
        return rst


执行结果:
     

两次结果对比可以看出效果还是有的,但是还是不行。


(1)按照上面的思路,就可以遍历得到结果了,但是要注意越界的情况,因为每次i加的数不一样,可能+1,也可能是+2,所以到最后时,谁也不确定循环结束后的i是指向最后一个字符,还是由于+2导致了越界,因此我们要做一个异常处理,如果越界,那一定是s[i+2]这个地方出错,说明此时的i就是s字符串的最后一个字符,那么我们直接在出错后,加上该字符对应的整数就可以了。

 

4. 解法三:

然后我去看了一下最优解的那个答案,它的代码是这样的

class Solution:
    def romanToInt(self, s):
        """
        :type s: str
        :rtype: int
        """
        romandict = dict({'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000})
        res = 0
        if len(s) == 1:
            return romandict[s[0]]            
        for i in range(len(s)-1):
            cur=s[i]
            nex=s[i+1]
            if((cur=='C' and (nex=='D' or nex=='M')) or (cur=='X' and (nex=='L' or nex=='C')) or (cur=='I' and (nex=='V' or nex=='X'))):
                res = res - romandict[cur]
            else:
                res = res + romandict[cur]
        res = res + romandict[nex]
        return res

 

额……我感觉好像没什么特别的方法,为什么时间只有124ms呢?我想起了之前几次做题得出的那个结论,就是python中自带的那些方法,比如索引之类的,可能本身是有点耗时的。比如我们要判断一个字符是不是在一个字符串中,那我们可能是用in这个方法,但是细想,为什么这样就可以判断是不是在字符串中呢。按照正常的逻辑,那应该是in这种方法本身就是去遍历一遍这个字符串,才会确定某个字符是不是在一个字符串中。

如果按照我的这种思路,那么他的这个代码优于我的原因可能就是这段代码:

if((cur=='C' and (nex=='D' or nex=='M')) or (cur=='X' and (nex=='L' or nex=='C')) or (cur=='I' and (nex=='V' or nex=='X'))):


因为我的那个字典太长,每次判断s[i]是否在roma_dict中都会去循环这个字典,而他的这个就可以不用循环这么多。多以我将代码改了一下

代码三:

class Solution:
    def romanToInt(self, s):
        roma_dict1 = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}
        roma_dict2 = {"IV": 4, "IX": 9, "XL": 40, "XC": 90, "CD": 400, "CM": 900, }
        i = 0
        rst = 0
        while i < len(s):
            try:
                if s[i] + s[i+1] in roma_dict2:
                    rst += roma_dict2.get(s[i]+s[i+1])
                    i += 2
                else:
                    rst += roma_dict1.get(s[i])
                    i += 1
            except:
                rst += roma_dict1.get(s[i])
                break
        return rst


我先不放执行结果,先说这个代码有哪些变化。就只是将roma_dict分成了两个。一个是单个罗马数字与对应的整数构成的字典,一个是两个罗马数字与对应的一个整数构成的字典,这样当判断s[i]是否在字典里的时候,要遍历的次数就少了。

举个例子,加入你要找文件。一种方法是所有东西都放在一个目录下,那你每次都要从一个目录中找出你要的文件。另一种方法是将不同性质的文件放在了不同名的目录下,每次只要你做一个简单的判断你要的文件是什么性质的,然后去对应的目录找,这样会快很多。

如果我们这种想法没错的话,那么这次的结果应该会比之前的要好,而且给的这个罗马数字越长效果越明显。
执行结果:
    

可以看到,效果有了很大的提高。这也验证之前分析的那些东西。

最后我也看了一些在前面的那些代码,都大同小异,不同的原因应该就在一些细节上吧,当然从上次我做的那个实验来看,这也和电脑配置有关。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大骨熬汤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值