在Leetcode上有两道题目是有关罗马数字的,一道是把罗马数字转换成阿拉伯数字,另一道是把阿拉伯数字转换成罗马数字。
具体来说是Leetcode 12题Integer to Roman和13题Roman to Integer。
具体的规则可见https://en.wikipedia.org/wiki/Roman_numerals或者https://baike.baidu.com/item/%E7%BD%97%E9%A9%AC%E6%95%B0%E5%AD%97/772296?fr=aladdin。
简单来说,是遵守左减右加的计算规则,即如果相邻的两个字母(罗马数字),如果左边的数字小于右边的数字,这两个字母的结果就是右边的数字减去左边的数字。如果是大于等于,则进行相加。
比较好的做题顺序是13->12(由易到难)。
那我们首先来做第13题。
主要的思路是
1. 根据基本规则建立哈希表。
2. 遍历罗马数字对应的字符串,将当前的字母对应的数字和前一个字母对应的数字进行比较。按照左减右加的规则进行运算。
class Solution { public: int romanToInt(string s) { if (s.size() == 0) { return 0; } int digits[] = {1, 5, 10, 50, 100, 500, 1000}; char symbols[] = "IVXLCDM"; unordered_map<char,int> hashmap; for(int i=0; i<sizeof(digits) / sizeof(int); ++i) { hashmap[symbols[i]] = digits[i]; } int num = 0; int prev = 1000; int cur = 0; for(int i=0; i<s.size(); ++i) { cur = hashmap[s[i]]; if (prev < cur) { num = num + cur - 2 * prev; } else { num = num + cur; } prev = cur; } return num; } };
接着我们来看第12题。
这道题做起来就没有那么容易了。那我们来看看一些特殊的数据,Ⅳ-4,Ⅸ-9,XL-40,XLI-41,XC-90,XCIII-93、XCV-95、XCVIII-98、XCIX-99等。我们发现无论是4和9这两个单独的数,还是处于[40, 50)或者[90, 100)是两个区间。都是需要先加后减,如果是区间,则加上个位数,最终得到正确的答案。
根据以上思路,代码如下所示:
class Solution { public: string getNumByFourOrNine(int &num, int base, int sub, unordered_map<int, char> &hashmap) { string res; if (num + sub < base) { return ""; } while (num >= base) { res = res + hashmap[base]; num -= base; } if (num + sub >= base) { res = res + hashmap[sub] + hashmap[base]; num = num + sub - base; } return res; } string intToRoman(int num) { int bases[7] = {1, 5, 10, 50, 100, 500, 1000}; char digits[8] = "IVXLCDM"; string res; unordered_map<int,char> hashmap; for (int i=0; i<sizeof(bases)/sizeof(int); ++i) { hashmap[bases[i]] = digits[i]; } for(int i = sizeof(bases) /sizeof(int) - 1; i >= 1; i--) { if (i % 2 == 0) { res += getNumByFourOrNine(num, bases[i], bases[i-2], hashmap); } else { res += getNumByFourOrNine(num, bases[i], bases[i-1], hashmap); } } while(num >= 1 && num<= 4) { res += "I"; --num; } return res; } };
上面的代码实现了功能,但还不够简化。主要是因为对于包含4和9的数,是现算的,而如果事先有表直接查询,则可减少运行时间,可参考如下代码:
class Solution { public: string intToRoman(int num) { string str; string symbol[]={"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"}; int value[]= {1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1}; for(int i=0;num>0;++i) { while(num-value[i]>=0) { num-=value[i]; str+=symbol[i]; } } return str; } };
是不是很简洁啊