zoukankan      html  css  js  c++  java
  • 【LeetCode】Roman to Integer & Integer to Roman

    Roman to Integer

    Given a roman numeral, convert it to an integer.

    Input is guaranteed to be within the range from 1 to 3999.


    首先学习一下罗马数字的规则:

    羅馬數字共有7個,即I(1)、V(5)、X(10)、L(50)、C(100)、D(500)和M(1000)。按照下述的規則可以表示任意正整數。需要注意的是罗马数字中没有“0”,與進位制無關。一般認為羅馬數字只用來記數,而不作演算。

    • 重複數次:一個羅馬數字重複幾次,就表示這個數的幾倍。
    • 右加左減:
      • 在較大的羅馬數字的右邊記上較小的羅馬數字,表示大數字加小數字。
      • 在較大的羅馬數字的左邊記上較小的羅馬數字,表示大數字减小數字。
      • 左减的数字有限制,仅限于I、X、C。比如45不可以写成VL,只能是XLV
      • 但是,左減時不可跨越一個位數。比如,99不可以用IC (100 - 1) 表示,而是用XCIX( (100 - 10) + (10 - 1) )表示。(等同於阿拉伯數字每位數字分別表示。)
      • 左減數字必須為一位,比如8寫成VIII,而非IIX。
      • 右加數字不可連續超過三位,比如14寫成XIV,而非XIIII。(見下方“數碼限制”一項。)
    • 加線乘千:
      • 在羅馬數字的上方加上一條橫線或者加上下標的Ⅿ,表示將這個數乘以1000,即是原數的1000倍。
      • 同理,如果上方有兩條橫線,即是原數的1000000(1000^2)倍。
    • 數碼限制:
      • 同一數碼最多只能出現三次,如40不可表示為XXXX,而要表示為XL。
      • 例外:由於IV是古羅馬神話主神朱庇特(即IVPITER,古羅馬字母裡沒有J和U)的首字,因此有時用IIII代替Ⅳ。

    这其中有一个很有意思的问题:

    对于这道题,真正有用的规则只有前面所述黑体的部分,那我们是“从前往后”扫描计算还是“从后往前”扫描呢?

    * 如果是从前往后,那么假定已经扫描过的位置都是已经处理了的(即这一位的值已经被计算过):

     1. 当 s[i] > s[i-1],应该是 s[i] - s[i-1],但是此时 s[i-1] 已经被处理并且一定是被加进了 ans (对 s[i-2] 分情况讨论便知),所以此时需要 ans += s[i] - 2*s[i-1];

     2. 当 s[i] <= s[i-1],应该是 加上 s[i-1],这没什么问题。

    * 如果是从后往前,那么假定已经扫描过的位置都是已经处理了的(即这一位的值已经被计算过):

     1. 当 s[i] >= s[i+1],把 s[i] 加进去即可( s[i+1] 是加进 ans 还是 减进 ans 的都没有影响),样例有 XCIX(99);

     2. 当 s[i] < s[i+1], 把 s[i] 减进 ans 即可。

    所以,相比之下“从后往前”的策略是更规范和统一,逻辑上也更清晰,ans 在处理过程中的波动理论上也更小,而且符合我们的假定:即只处理还未扫描到的位,已经扫描到的不再做任何 “补救” 形态的处理。

    下面,把两种策略的代码都贴出来:

    “从前往后”的策略:

     1 class Solution:
     2     # @param {string} s
     3     # @return {integer}
     4     def romanToInt(self, s):
     5         roman = {
     6             "I":    1,
     7             "V":    5,
     8             "X":    10,
     9             "L":    50,
    10             "C":    100,
    11             "D":    500,
    12             "M":    1000
    13             }
    14         ans = roman[s[0]]
    15         for i in range(1, len(s)):
    16             if roman[s[i]] > roman[s[i-1]]:
    17                 ans += roman[s[i]] - 2*roman[s[i-1]]
    18             else:
    19                 ans += roman[s[i]]
    20         return ans

    “从后往前”的策略:

     1 class Solution:
     2     # @param {string} s
     3     # @return {integer}
     4     def romanToInt(self, s):
     5         roman = {
     6             "I":    1,
     7             "V":    5,
     8             "X":    10,
     9             "L":    50,
    10             "C":    100,
    11             "D":    500,
    12             "M":    1000
    13             }
    14         n = len(s)
    15         ans = roman[s[n-1]]
    16         i = n-2
    17         while i >= 0:
    18             if roman[s[i]] >= roman[s[i+1]]:
    19                 ans += roman[s[i]]
    20             else:
    21                 ans -= roman[s[i]]
    22             i -= 1
    23         return ans

    Reference:

    http://blog.csdn.net/jellyyin/article/details/13165731


    Integer to Roman

    Given an integer, convert it to a roman numeral.

    Input is guaranteed to be within the range from 1 to 3999.

    Solution:

    反过来的关键在于找出能表示所有的数的“基本元”的集合,然后从数值大到小排列,依次从num里面减去,同时构造出罗马数字串,直至num为0。

    那么哪些是我们要找的“基本元”呢?

    让我们从小数字来看,首先 1 是已经存在的基本元(I),2和3则可以用 I 的重复来表示,但是 4 却已经不能用更 4 个 I 来表示了(受 “右加數字不可連續超過三位” 规则限制),而只能用 5-1 也就是 IV 来表示了。

    至此,6 - 8 也可以用 V 和 I 的组合表示了,但是对于 9,首先不能是 VIIII,5+4(VIV)也不行( I 位的符号受“右加左减”限制存在矛盾),而只能是 10 - 1 (IX)。

    至此,个位数的情况已经穷尽,而更高数量级的数字与此同理可以找出“基本元”,较小数量级的数又可以由已经找出的“基本元”的合法组合来表示,这就好比我们熟知的人民币数值系统。

    代码如下:

     1 class Solution:
     2     # @param {integer} num
     3     # @return {string}
     4     def intToRoman(self, num):
     5         values = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1]
     6         numerals = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"]
     7         ans, i = "", 0
     8         while num:
     9             ans += (num//values[i]) * numerals[i]
    10             num %= values[i]
    11             i += 1
    12         return ans
  • 相关阅读:
    Linux 下的类似Windows下Everything的搜索工具
    windows和linux环境下制作U盘启动盘
    程序调试手段之gdb, vxworks shell
    LeetCode 1021. Remove Outermost Parentheses (删除最外层的括号)
    LeetCode 1047. Remove All Adjacent Duplicates In String (删除字符串中的所有相邻重复项)
    LeetCode 844. Backspace String Compare (比较含退格的字符串)
    LeetCode 860. Lemonade Change (柠檬水找零)
    LeetCode 1221. Split a String in Balanced Strings (分割平衡字符串)
    LeetCode 1046. Last Stone Weight (最后一块石头的重量 )
    LeetCode 746. Min Cost Climbing Stairs (使用最小花费爬楼梯)
  • 原文地址:https://www.cnblogs.com/maples7/p/4732762.html
Copyright © 2011-2022 走看看