【前端也得会算法】13. 整数转罗马数字[ 中等 ]

126 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第6天,点击查看活动详情

一、题目描述:

罗马数字包含以下七种字符: I, V, X, LCD 和 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) 的左边,来表示 49。
X 可以放在 L (50) 和 C (100) 的左边,来表示 40 和 90。 
C 可以放在 D (500) 和 M (1000) 的左边,来表示 400 和 900

给定一个罗马数字,将其转换成整数。

示例:

示例 1:
输入: s = "III"
输出: 3

示例 2:
输入: s = "IV"
输出: 4

示例 3:
输入: s = "IX"
输出: 9

示例 4:
输入: s = "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.

示例 5:
输入: s = "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.

提示:

  • 1 <= s.length <= 15
  • s 仅含字符 ('I', 'V', 'X', 'L', 'C', 'D', 'M')
  • 题目数据保证 s 是一个有效的罗马数字,且表示整数在范围 [1, 3999] 内
  • 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
  • IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX 。
  • 关于罗马数字的详尽书写规则,可以参考 罗马数字 - Mathematics 。

二、题解:

这道题将罗马数字字符串转化为数值,意味着得匹配字符串数字对应的数值之和即是结果。

这里列出所有特殊情况,然后择二匹配即可,不能匹配则择一匹配即可。

注意6种特殊情况

方法一 截取叠加法

  • 原理。根据题解循环匹配叠加即可。
  • 思路。
    • 截取两位判断是否是特殊字符
    • 是的话叠加数值,遍历序号+2
    • 不是的话叠加数值,遍历序号+1

代码:

var romanToInt = function(s) {
  let map = {
    'I': 1,
    'V': 5,
    'X': 10,
    'L': 50,
    'C': 100,
    'D': 500,
    'M': 1000,
    'IV': 4,
    'IX': 9,
    'XL': 40,
    'XC': 90,
    'CD': 400,
    'CM': 900,
  }
  let num = 0
  let index = 0
  // 遍历的边界
  while(index<s.length){
      // 先截取2位 
    let ch = s.substring(index, index+2)
    // 如果2位都在map里,则是特殊情况需要index+2
    if(ch in map){
      num += map[ch]
      index += 2
    } else {
     // 如果2位不在map里,则截取一位出结果即可
      ch = s.substring(index, index+1)
      num += map[ch]
      index++
    }
    
  }
  return num
};

image.png

方法二 巧用遍历法

  • 原理。根据题解利用大小关系遍历时叠加后剪短字符串即可。
  • 思路。
    • 声明一个从大到小的map
    • 循环map
    • 当遍历到匹配序号是第0个时,则开始叠加法
    • 记录后减短字符串
    • 不断叠加则得出结果

代码:

var intToRoman = function(num) {
     let map = new Map([
    [1000, 'M'],
    [900, 'CM'],
    [500, 'D'],
    [400, 'CD'],
    [100, 'C'],
    [90, 'XC'],
    [50, 'L'],
    [40, 'XL'],
    [10, 'X'],
    [9, 'IX'],
    [5, 'V'],
    [4, 'IV'],
    [1, 'I'],
  ])
  let num = 0
  for(let [key, value] of map){
      // 如果当前匹配序号是第0个,则还在循环内
    while(s.indexOf(value) === 0){
      num += key
      // 截取匹配字符串下标
      s = s.substring(value.length)
    }
  }
  return num
}

image.png

三、总结

  • 此题可以截取叠加法巧用遍历法两种方案
  • 截取叠加法主要是根据题解循环匹配叠加即可。
  • 巧用遍历法主要是根据题解利用大小关系遍历时叠加后剪短字符串即可。

文中如有错误,欢迎在评论区指正