20201229力扣刷题-罗马数字转整数

罗马数字包含以下七种字符: 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 的范围内。

 

示例 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.
 

提示:

题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
IC 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。

 

审题:

1、只有6种情况是相减,其他相加

2、不管6种特殊情况是否出现,罗马数字都是叠加的

 

我的答案:

class Solution:
    def romanToInt(self, s: str) -> int:
        dict = {}
        dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
        list = ['IV', 'IX', 'XL', 'XC', 'CD', 'CM']
        part = 0
        s_split = []

        for l in range(6):
            if s.find(list[l]) != -1:
                de_index = s.find(list[l])
                part += dict[list[l][1]] - dict[list[l][0]]
                s = s.replace(s[de_index]+s[de_index+1],'')
            else:
                pass

        for i in range(len(s)):
            s_split.append(s[i])

        num = part
        for j in s_split:
            num += dict[j]

        return num

基本思路:先筛选出特殊情况,算出减法部分的数字,同时在原字符串中去掉减法部分,只留加法部分。之后处理加法部分,即分割字符串,对应到字典再相加即可。

注意:新建空列表、设置初始值一定要放在循环之外!!

其他解法:

class Solution:
    def romanToInt(self, s: str) -> int:
        dict = {}
        dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
        s_split = []
        num = 0

        for i in range(len(s)):
            s_split.append(s[i])

        for j in range(len(s)):
            try:
                value_j = dict[s_split[j]]
            except Exception:
                value_j = 0

            try:
                value_J = dict[s_split[j + 1]]
            except Exception:
                value_J = 0

            if value_j <value_J:
                num += (value_J - value_j)
                s_split.pop(j + 1)
            else:
                num += value_j


        return num

在官网看大神解答是用java写的,自己按思路重写了一遍,运行成功,但发现几处问题。

基本思路:遍历然后比较每个数和后一个数的大小,如果大数在前则做加法,如果大数在后则做减法。

问题:

1、大数在后,处理的单元为2,如何在循环中跳过下一次循环?这里用了pop加异常处理的方式

2、另一处异常处理来自j+1在最后一位会超出循环,同样叠加了异常处理

最大的问题:结果性能不升反降……

 

重新调试:

发现前<后时,不需要用后-前这个死板的方法,对公式做个小小的变形(我咋就没想到呢),前面一个数字减,后面一个数字加,就不会出现后一位重复加的问题了。

改过一轮的代码如下,性能有略微提升:

class Solution:
    def romanToInt(self, s: str) -> int:
        dict = {}
        dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
        s_split = []
        num = 0

        for i in range(len(s)):
            s_split.append(s[i])

        for j in range(len(s)-1):
            if dict[s_split[j]] >= dict[s_split[j+1]]:
                num += dict[s_split[j]]
            else:
                num -= dict[s_split[j]]
        num += dict[s_split[len(s)-1]]

        return num

其他解法:使用get函数和enumerate遍历(优势在于写法简练)

1、除了第0位以外,每次取本位和前一位双元素。如果按原值取和会造成双元素前位取两遍,所以双元素在字典中的值要多减一个前位。(同样是数学变形的思路)

2、get函数的优势在于可以二选一,也就是如果没有找到该键值,则用默认值替代,这里就是如果字典里没有找到对应双元素,就取本位的值用于相加。

class Solution:
    def romanToInt(self, s: str) -> int:
        dict = {}
        dict['I'], dict['V'], dict['X'], dict['L'], dict['C'], dict['D'], dict['M'] = 1, 5, 10, 50, 100, 500, 1000
        dict['IV'],dict['IX'],dict['XL'],dict['XC'],dict['CD'],dict['CM'] = 3,8,30,80,300,800
        for i, n in enumerate(s):
            print(s[max(i-1,0):i+1])
            print(dict.get(s[max(i-1,0):i+1],dict[n]))
        return sum(dict.get(s[max(i-1,0):i+1],dict[n]) for i,n in enumerate(s))

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值