【算法攻坚】整数转罗马数字

271 阅读2分钟

这是我参与11月更文挑战的第14天,活动详情查看:2021最后一次更文挑战

今日题目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:

输入: 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

思路

罗马数字的组合个数是有限的,所以可以通过把有限的状态存储起来,然后把数字拆解成基本的罗马数字状态,把罗马数字的组合类比成阿拉伯数字的位(个十百千....),

然后通过把数字依次递减的用最大“位"来凑出这个输入的数字

同时,要保证存储的状态是从大到小排列的,这样就可以方便的吧数字优先通过最大的罗马数字组合而成

此时就想到了Java中的LinkedHashMap来实现

整体的思想用的是贪心思想,总是想从最大的开始,类似的题目还有找零钱。其实整体难度不算太大,如果能找到组合规律,应该很快就能写出来,

在手写的时候遇到个小问题,在用惯了IDEA写代码后,发现手写map的循环还是有难度的,因为会设计map的内部类entry,需要多练习几遍

public String intToRoman(int num) {
    //结果
    StringBuilder result = new StringBuilder();
    //构造状态
    Map<Integer, String> int2roman = initData();
    Set<Map.Entry<Integer,String>> entrys = int2roman.entrySet();
    for(Map.Entry<Integer, String> entry: entrys){
        while(num >= entry.getKey()){
            //也可以用除法的方案
            num = num -entry.getKey();
            result.append(entry.getValue());
        }
    }
    return result.toString();
}

private  Map<Integer, String> initData(){
    //必须保证有序,可以用两个数组、链表、LinkedHashMap等保证有序
    Map<Integer, String> int2roman = new LinkedHashMap<>();
    int2roman.put(1000, "M");
    int2roman.put(900, "CM");
    int2roman.put(500, "D");
    int2roman.put(400, "CD");
    int2roman.put(100, "C");
    int2roman.put(90, "XC");
    int2roman.put(50, "L");
    int2roman.put(40, "XL");
    int2roman.put(10, "X");
    int2roman.put(9, "IX");
    int2roman.put(5, "V");
    int2roman.put(4, "IV");
    int2roman.put(1, "I");
    return int2roman;
}

执行用时:6 ms, 在所有 Java 提交中击败了30.10%的用户

内存消耗:37.9 MB, 在所有 Java 提交中击败了47.39%的用户

解法二

还有一种暴力解法,主要是利用了输入1 <= num <= 3999,在一定的范围内,所以直接把不同的位数组合起来,这种解法我觉没啥必要学习,也不够通用,这里就不在列举了,有兴趣的可以再自己写写试试。

小结

贪心算法是解决寻找全局最优解时先确定局部最优解,然后把局部最优解不断迭代,从而获得整体最优解,

这样题目也有很多,在遇到几个类似的时候,我们再尝试总结下它们的特性和解题方法。

今天多学一点,明天就少说一句求人的话,加油!