罗马数字转整数的算法

罗马数字转整数功能的实现

题目

# 罗马数字包含以下七种字符: 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。 IV=4 IX=9
# X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 XL=40 XC=90
# C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900。CD=400 CM=900
# 给定一个整数,将其转为罗马数字。输入确保在 1 到 3999 的范围内。
#
# 来源:力扣(LeetCode)
# 链接:https://leetcode-cn.com/problems/roman-to-integer
# 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

思路

拿到这道题时,我首先想的是建立一个roman数列,里面从小到大保存着所有的罗马字符,同时建立一个字典从小到大保存着罗马字符以及其对应的数量大小。如下所示:

roman = ['I', 'V', 'X', 'L', 'C', 'D', 'M']
roman_value = {'I': 1,
               'V': 5,
               'X': 10,
               'L': 50,
               'C': 100,
               'D': 500,
               'M': 1000
               }

解题思路则是,将拿到的罗马数字用for循环取值,一次取两个。若是取到的第一个字符比第二个字符小,则加上第二个字符代表的值减去第一个字符代表的值(如IX=10-1=9),否则加上第二个数字的值。然后再判断第二个字符和第三个字符......

实现算法

出错算法

首次解题时,我写出了如下算法:

def romanToInt(s):
    res = 0
    for i in s:  # i:第一个数
        if s.index(i) != len(s) + 1:
            j = s[s.index(i) + 1] #出错 由于X出现了两次,当取i='X'时,index(i)会默认取第一个'X'的索引
            if roman_value[i] < roman_value[j]: 
                res += roman_value[j] - roman_value[i]
                res -= roman_value[j]
                # 由于第二次循环还是会循环到j,相当于再加了一次j,所以直接先减去以抵消
            else:
                res += roman_value[i]
        else:
            res += roman_value[i]
    return res



在转换‘MXCIX’,也就是罗马字符的1099时,输出的答案为1079。然后我debug出了一个我无法解决bug。

我使用了.index的方法,也就是根据每次循环取到的具体字符 i 来反推这个字符在原字符串 s 中的索引,并且根据这个索引+1的值来取到下一个值.但是我在使用这种方法的时候没有考虑到字符可能重复出现的情况.就比如在转换‘MXCIX’时,当 i 取到第二个X(高亮X)时, s.index(i)任然是第一个X的索引,导致程序出错.

这个bug以我现在的知识无法解决,于是我在查阅一些资料后写出了改进算法:

改进算法

def romanToInt(s):
    res = 0
    for i,v in enumerate(s):  # i:索引 v:值
        if i != len(s)-1:
            j = s[i + 1]   #j: s中以i+1为索引的值
            if roman_value[v] < roman_value[j]:
                res += roman_value[j] - roman_value[v]
                res -= roman_value[j]
            else:
                res += roman_value[v]
        else:
            res += roman_value[v]
    return res

主要改进是使用了enumerate(枚举)这个方法,它的作用是分别返回索引以及值.这样就在循环的一开始确定了索引和值,而不是通过值反推索引,这样会因为重复出现的值出bug.

反思:enumerate真好真好用啊

原文地址:https://www.cnblogs.com/Sheppard/p/11264544.html