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

161 阅读3分钟

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

一、题目描述:

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

给你一个整数,将其转为罗马数字。

示例:

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

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

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

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

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

提示:

  • 1 <= num <= 3999

二、题解:

这道题将数值转化为罗马数字字符串,意味着得从右至左或从左至右拼接数字。

这里找出千位,百位,十位,个位然后拼接起来就行

注意6种特殊情况

方法一 位数匹配法

  • 原理。根据题解循环匹配位数即可。
  • 思路。
    • 先写出数字对应的罗马数字字符串
    • 记录千百十个位
    • 取第一位判断是否有千百十位
    • 是否是特殊情况
    • 整数位是否大于5*tenCount
    • 循环叠加即可

代码:

var intToRoman = function(num) {
  let map = {
    '1': 'I',
    '5': 'V',
    '10': 'X',
    '50': 'L',
    '100': 'C',
    '500': 'D',
    '1000': 'M',
    '4': 'IV',
    '9': 'IX',
    '40': 'XL',
    '90': 'XC',
    '400': 'CD',
    '900': 'CM',
  }
  let res = ''
  let count = 0
  let cArr = [1000,100,10,1]
  while(num>0){
    let tenCount = cArr[count]
    // 取第一位
    let n = ~~(num / tenCount)
    // 获得整数位
    let allN = n*tenCount
    let curStr = ''
    // 为0则没有这一位
    if(n===0){
      count++
      continue
    }
    // 是否是特殊情况
    if(allN in map){
      curStr = map[allN]
    } else {
      // 整数位是否大于5*tenCount
      if(allN>=5*tenCount){
        curStr += map[5*tenCount]
        while(n-5>0){
          curStr += map[1*tenCount]
          n--
        }
      } else {
        while(n>0){
          curStr += map[1*tenCount]
          n--
        }
      }
    }
    res += curStr
    num = num % tenCount
    count++
  }
  return res
};

如果替换成字符串呢,可以使用截取来获取整数位。

修改后的代码:

var intToRoman = function(num) {
  let map = {
    '1': 'I',
    '5': 'V',
    '10': 'X',
    '50': 'L',
    '100': 'C',
    '500': 'D',
    '1000': 'M',
    '4': 'IV',
    '9': 'IX',
    '40': 'XL',
    '90': 'XC',
    '400': 'CD',
    '900': 'CM',
  }
    let res =''
    num = num +''
  while(num.length>0){
    let tenCount = Math.pow(10,num.length-1)
    // 取第一位
    let n = num[0]
    // 获得整数位
    let allN = n*tenCount
    let curStr = ''
    // 是否是特殊情况
    if(allN in map){
      curStr = map[allN]
    } else {
      // 整数位是否大于5*tenCount
      if(allN>=5*tenCount){
        curStr += map[5*tenCount]
        while(n-5>0){
          curStr += map[1*tenCount]
          n--
        }
      } else {
        while(n>0){
          curStr += map[1*tenCount]
          n--
        }
      }
    }
    res += curStr
    num = num.substr(1)
  }
  return res
};

image.png

方法二 巧用遍历法

  • 原理。根据题解利用大小关系遍历时记录后相减。
  • 思路。
    • 声明一个从大到小的map
    • 循环map
    • 当遍历到num大于map的key时,则开始记录
    • 记录后减去改数值,则开始余数记录
    • 不断相减记录则得出结果

代码:

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 res = ''
  for(let [key,value] of map){
    while(num>= key){
      res+= value
      num -= key
    }
  }
  return res
};

image.png

三、总结

  • 此题可以位数匹配法巧用遍历法两种方案
  • 位数匹配法主要是根据题解循环匹配位数即可。
  • 巧用遍历法主要是根据题解利用大小关系遍历时记录后相减。

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