力扣12-整数转罗马数字
原题链接:https://leetcode.cn/problems/integer-to-roman/
题目描述
罗马数字包含以下七种字符: 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
解题
首先要明白有多少种的罗马数字符号,除了 I, V, X, L,C,D 和 M,还有5-1
,10-1
,50-10
,100-10
等情况,一共是十三种
"I","IV","V","IX","X","XL","L","XC","C","CD","D","CM","M"
分别对应的数值为:
1,4,5,9,10,40,50,90,100,400,500,900,1000
我们要做的,就是从最大值,也就是最右端开始,对比原整数,如果原整数大于该值,则创建字符串并追加对应的罗马数字,举个例子:
- 整数是21
- 对比最右端M对应的1000,21小于1000,换成CM对应的900,继续对比
- 以此类推,直到移动到罗马数字X时,21>10,所以结果字符串目前修改为X,整数修改为11
- 继续判断X,结果字符串修改为XX,整数修改为1
- 继续移动到I,结果字符串修改为XXI,整数修改为0
- 结束循环,返回字符串XXI
需要注意的是:
- 不是碰到小于自身的罗马数字就跳出循环,比如x=3时,需要替换三次I
- 是从最大值到最小值检索
- 需要使用
const char*
来接收罗马数字组成的数组 - 结果字符串在声明时使用动态内存的方法申请空间,从
const char*
类型的字符串复制时需用strcpy函数 - 或使用calloc申请空间,默认填充为0;
力扣给的难度是中等题,更麻烦的是如何化简代码,如果用很多if,会显得很臃肿。
我们可以将值存到数组中,使用下标访问。
敲代码
解题方法其实很多,上面只是其中一种
C语言的字符串操作比较麻烦,应注意使用strcat、strcpy、strncpy等字符串以及动态内存的相关方法
char* intToRoman(int num) { char* result = (char*)calloc(20,sizeof(char)); int key[] = { 1,4,5,9,10,40,50,90,100,400,500,900,1000 }; const char* value[] = {"I","IV","V","IX","X","XL","L","XC","C","CD","D","CM","M"}; for (int i = sizeof(value) / sizeof(value[0])-1; i >=0 ; i--) { while (num >= key[i]) { num -= key[i]; strcat(result, value[i]); } } return result; }
运行结果
执行用时:4 ms, 在所有 C 提交中击败了80.14%的用户
内存消耗:5.8 MB, 在所有 C 提交中击败了49.10%的用户
通过测试用例:3999 / 3999
力扣13-罗马数字转整数
原题链接:https://leetcode.cn/problems/roman-to-integer/
题目描述
罗马数字包含以下七种字符: 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:
输入: 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 。
解题
在解完上一道整数转罗马数字的题目后,看到这个题,是否可以使用上一题的方法?设:
const char* key[] = { "I","IV","V","IX","X","XL","L","XC","C","CD","D","CM","M" };
int value[] = { 1,4,5,9,10,40,50,90,100,400,500,900,1000 };
我们循环检索字符串中是否有key值,有则修改结果整型,填充原字符串中的重复位置为无关字符。
那么,问题便出现了,key数组的最右侧是M。
假设现在有MMM和MCM两个罗马数字,第一个很明显会返回3000,但第二个字符串可能会返回2100,因为在检索M时无法避免混淆独立的M和CM中的M。
每次检索时都需要加额外的限制条件,因此,我们换一种方法。
对于罗马数字,如IV,如果左侧字母小于右侧,那么便是减去数值小的字母,反之则为加
对于最后一个字符,右侧没有与之对比的字符,并且也不可能是减,可以在循环外单独添加
由于每次对比需要两个下标,因此循环结束条件应是strlen(s)-2
,避免最后一个字符对比时越界
比如:IVI
- 首先,这个罗马数字是不存在的,可以直接写成V
- 其次,用上面的方法,也能求出来5:I小于V,V大于I,最后一个字符为I,单独加入
- 结果为
-1+5+1=5
上代码
代码中
value['I' - 'A']
是为了压缩空间,能够根据字符快速的索引对应的数值
int romanToInt(char* s) { int result = 0; int value[26]; value['I' - 'A'] = 1; value['V' - 'A'] = 5; value['X' - 'A'] = 10; value['L' - 'A'] = 50; value['C' - 'A'] = 100; value['D' - 'A'] = 500; value['M' - 'A'] = 1000; for (int i = 0; i < strlen(s) - 1; i++) { if (value[s[i] - 'A'] >= value[s[i + 1] - 'A']) result += value[s[i] - 'A']; else result -= value[s[i] - 'A']; } result += value[s[strlen(s) - 1] - 'A']; return result; }
运行结果
执行用时:8 ms, 在所有 C 提交中击败了55.51%的用户
内存消耗:5.8 MB, 在所有 C 提交中击败了18.16%的用户
通过测试用例:3999 / 3999
总结
两个题目看起来很像,一个是字符串转整型,一个是整型转字符串,使用的方法不同。
回文数的时候,如果传入形式分别为字符串和整型,处理方法也不同。
C语言中字符串和内存操作比较麻烦,应该数量掌握常用的字符串和内存方法,以及还应注意const char*
和char*
直接的类型转换问题。
目前我的方法就是,如果处理字符串,就逐字符操作,初始化、赋值、修改用动态内存和strncpy的方法(因为直接赋值是const char*
,无法修改,需要从字符常量区搬到栈区或者堆区)
如果处理整型,比较容易,符合条件加减即可