1.相关题目:
#13 罗马数字转整数 简单难度
#12 整数转罗马数字 中等难度
其中13题描述如下
罗马数字包含以下七种字符: 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 到 3999 的范围内。
示例 1:
输入: "III"
输出: 3
示例 2:
输入: "IV"
输出: 4
示例 3:
输入: "IX"
输出: 9
示例 4:
输入: "LVIII"
输出: 58
解释: L = 50, V= 5, III = 3.
示例 5:
输入: "MCMXCIV"
输出: 1994
解释: M = 1000, CM = 900, XC = 90, IV = 4.
2.罗马数字转整数
2.1评论获赞最多的解法
归纳出一般规律,即从左往右遍历字符串,如果当前字符对应的数不小于其下一个字符对应的数,则加上该数;否则减去该数
复杂度:时间复杂度应为O(n),空间复杂度应为O(1)
python3实现
def romanToInt(self, s: str) -> int:
dic = {'I':1, 'V':5, 'X':10, 'L':50, 'C':100, 'D':500, 'M':1000}
num = 0
for i in range(len(s)):
if i < len(s)-1 and dic[s[i]] < dic[s[i+1]]:
num -= dic[s[i]]
else:
num += dic[s[i]]
return num
C++实现
int romanToInt(string s) {
map<char, int> hashMap = {{'I', 1}, {'V', 5}, {'X', 10},
{'L', 50}, {'C', 100}, {'D', 500}, {'M', 1000}};
int i = 0;
int ans = 0;
while(i < s.size()-1)
{
if(hashMap[s[i]] >= hashMap[s[i+1]])
ans += hashMap[s[i]];
else
ans -= hashMap[s[i]];
i++;
}
ans += hashMap[s[i]];
return ans;
}
2.2 用时较短的解法
先处理输入s是单个字符的情况,然后遍历s,优先检查相邻两个字符是否在dic中,是则转换两个字符,否则转换单个字符,注意最后的情况
python实现
def romanToInt(self, s: str) -> int:
romanDic = {'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}
i, num = 0, 0
if len(s) == 1:
return romanDic[s[0]]
while i < len(s)-1:
if s[i:i+2] in romanDic:
num += romanDic[s[i:i+2]]
i += 2
continue
num += romanDic[s[i]]
i += 1
if i < len(s):
num += romanDic[s[-1]]
return num
C++实现:没有使用map容器,而是建了一个int数组将字符串中的各字符转换成对应的罗马数
int romanToInt(string s) {
int sum = 0;
int n[s.size()+1];
n[s.size()]=0;
for(int i = 0; i < s.length(); i++){
switch(s[i]){//也可以用int n[100]; n['I']=1;....表示
case 'I': n[i]=1; break;
case 'V': n[i]=5; break;
case 'X': n[i]=10; break;
case 'L': n[i]=50; break;
case 'C': n[i]=100; break;
case 'D': n[i]=500; break;
case 'M': n[i]=1000; break;
default :break;
}
}
for(int i = 0; i < s.length(); i++){
if(n[i] < n[i+1]) {
sum = sum + n[i+1] - n[i];
i++;
}else
sum += n[i];
}
return sum;
}
2.3 自己的解法(不是非常好,故列在最后)
遍历字符串,当读到的字符是’I’,‘X’,'C’时,判断其后字符是否属于特殊情况
python实现
def romanToInt(self, s: str) -> int:
# 自己的解法
romanDic = {'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}
i, num = 0, 0
biRomanList = ["IV", "IX", "XL", "XC", "CD", "CM"]
while i<len(s): # 代码注释会影响执行用时(包括注释的代码行越短越好)
if s[i] in ['I', 'X', 'C']:
if i<len(s)-1 and s[i]+s[i+1] in biRomanList:
num += romanDic[s[i]+s[i+1]]
i += 1
else:
num += romanDic[s[i]]
else:
num += romanDic[s[i]]
i += 1
return num
3.整数转罗马数字
3.1 简洁高效的思路
先尽量减大的数并连接对应的罗马字符串,直到不够减了再减小的数
python实现
class Solution:
def intToRoman(self, num: int) -> str:
"""
先按降序建立一个(罗马字符,对应数字)的元祖列表,然后用for循环遍历该列表,
在for循环内将num不断减去当前映射对中的数字n并将结果加上对应罗马字符直到小于n
"""
ans = ''
romans = [(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')]
for n,c in romans:
while num >= n:
num -= n
ans += c
return ans
C++实现
class Solution {
public:
string intToRoman(int num) {
string ans;
vector<int> ivec{1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1};
vector<string> svec{"M", "CM", "D", "CD", "C", "XC", "L", "XL", "X",
"IX", "V", "IV", "I"};
for(int i=0; i<ivec.size(); ++i)
while(num >= ivec[i])
{
num -= ivec[i];
ans += svec[i];
}
return ans;
}
};
3.2 获赞最多的思路
确定千、百、十、个位的数字对应的罗马字符串,然后依据输入数字的各位数将相应的罗马字符串连接起来(有点类似分解整数的各位)
python实现(https://leetcode-cn.com/problems/integer-to-roman/comments/3765):
class Solution:
def intToRoman(self, num):
m = [
['', 'M', 'MM', 'MMM'],
['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'],
['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
['', 'I', 'II', 'III', 'IV', 'V', 'VI', 'VII', 'VIII', 'IX']
]
d = [1000, 100, 10, 1]
r = ''
for k, v in enumerate(d):
r += m[k][int(num/v)]
num = num % v
return r
C++实现:
class Solution {
public:
string intToRoman(int num) {
string ans="";
char* romans[4][10] = {{"", "M", "MM", "MMM"},
{"", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM"},
{"", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC"},
{"", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX"}};
vector<int> ivec{1000, 100, 10, 1};
for(int i=0; i<ivec.size(); ++i)
{
ans += romans[i][num/ivec[i]];
num %= ivec[i];
}
return ans;
}
};
3.3 自己的思路:
1.先将数字转换成字符串
2.为个、十、百位分别建立映射字典,然后根据数字及其所在的位获取对应的罗马字符串并将字符串连接起来
class Solution:
def __init__(self):
self.dic1 = {'1':'I', '4':'IV', '5':'V', '9':'IX'}
self.dic2 = {'1':'X', '4':'XL', '5':'L', '9':'XC'}
self.dic3 = {'1':'C', '4':'CD', '5':'D', '9':'CM'}
self.dicList = [self.dic1, self.dic2, self.dic3]
def getStr(self, digit: int, pos: int) -> str:
res = ''
if pos > 3 or pos < 0:
return res
if pos == 3:
res = 'M'*digit
else:
if digit < 1 or digit > 9:
return res
elif digit < 4:
res = self.dicList[pos]['1']*digit
elif digit == 4:
res = self.dicList[pos]['4']
elif digit == 5:
res = self.dicList[pos]['5']
elif digit < 9:
res = self.dicList[pos]['5'] + self.dicList[pos]['1']*(digit-5)
else:
res = self.dicList[pos]['9']
print(res)
return res
def intToRoman(self, num: int) -> str:
"""
自己的思路:
1.先将数字转换成字符串
2.为个、十、百位分别建立映射字典,然后根据数字及其所在的位获取对应的罗马字符串
并将字符串连接起来
"""
ans = ''
sNum = str(num)
for i in range(len(sNum)):
ans += self.getStr(int(sNum[i]), len(sNum)-1-i)
return ans
总结:不管是罗马数字转整数还是整数转罗马数字,其实都可以看成一个编码映射的问题,弄清楚相应的编码转换规则(如罗马数字转整数时左边字符小于右边字符对应的数字则用减否则用加,又如整数转罗马数字时建立整数各位数字对应的罗马字符串码本)便不难实现。
注:以上解法均来自leetcode网站以及自己的一些修改,参考链接不方便注明故此省去,如有侵权还望见谅