力扣12&13-整数与罗马数字互换

力扣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-110-150-10100-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
我们要做的,就是从最大值,也就是最右端开始,对比原整数,如果原整数大于该值,则创建字符串并追加对应的罗马数字,举个例子:

  1. 整数是21
  2. 对比最右端M对应的1000,21小于1000,换成CM对应的900,继续对比
  3. 以此类推,直到移动到罗马数字X时,21>10,所以结果字符串目前修改为X,整数修改为11
  4. 继续判断X,结果字符串修改为XX,整数修改为1
  5. 继续移动到I,结果字符串修改为XXI,整数修改为0
  6. 结束循环,返回字符串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
力扣12&13-整数与罗马数字互换_第1张图片

力扣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
力扣12&13-整数与罗马数字互换_第2张图片

总结

两个题目看起来很像,一个是字符串转整型,一个是整型转字符串,使用的方法不同。
回文数的时候,如果传入形式分别为字符串和整型,处理方法也不同。
C语言中字符串和内存操作比较麻烦,应该数量掌握常用的字符串和内存方法,以及还应注意const char*char*直接的类型转换问题。
目前我的方法就是,如果处理字符串,就逐字符操作,初始化、赋值、修改用动态内存和strncpy的方法(因为直接赋值是const char*,无法修改,需要从字符常量区搬到栈区或者堆区)
如果处理整型,比较容易,符合条件加减即可

你可能感兴趣的:(C/C++解题,leetcode,算法,数据结构)