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]是否在字典里的时候,要遍历的次数就少了。
举个例子,加入你要找文件。一种方法是所有东西都放在一个目录下,那你每次都要从一个目录中找出你要的文件。另一种方法是将不同性质的文件放在了不同名的目录下,每次只要你做一个简单的判断你要的文件是什么性质的,然后去对应的目录找,这样会快很多。
如果我们这种想法没错的话,那么这次的结果应该会比之前的要好,而且给的这个罗马数字越长效果越明显。
执行结果:


可以看到,效果有了很大的提高。这也验证之前分析的那些东西。
最后我也看了一些在前面的那些代码,都大同小异,不同的原因应该就在一些细节上吧,当然从上次我做的那个实验来看,这也和电脑配置有关。