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]是否在字典裏的時候,要遍歷的次數就少了。

舉個例子,加入你要找文件。一種方法是所有東西都放在一個目錄下,那你每次都要從一個目錄中找出你要的文件。另一種方法是將不同性質的文件放在了不同名的目錄下,每次只要你做一個簡單的判斷你要的文件是什麼性質的,然後去對應的目錄找,這樣會快很多。

如果我們這種想法沒錯的話,那麼這次的結果應該會比之前的要好,而且給的這個羅馬數字越長效果越明顯。
執行結果:
    

可以看到,效果有了很大的提高。這也驗證之前分析的那些東西。

最後我也看了一些在前面的那些代碼,都大同小異,不同的原因應該就在一些細節上吧,當然從上次我做的那個實驗來看,這也和電腦配置有關。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章