0013. 罗马数字转整数【简单】
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:
输入:s = "III"
输出:31
2
2
示例 2:
输入:s = "IV"
输出:41
2
2
示例 3:
输入:s = "IX"
输出:91
2
2
示例 4:
输入:s = "LVIII"
输出:581
2
2
解释:L = 50, V= 5, III = 3.
示例 5:
输入:s = "MCMXCIV"
输出:19941
2
2
解释:M = 1000, CM = 900, XC = 90, IV = 4.
提示:
1 <= s.length <= 15s仅含字符('I', 'V', 'X', 'L', 'C', 'D', 'M')- 题目数据保证
s是一个有效的罗马数字,且表示整数在范围[1, 3999]内 - 题目所给测试用例皆符合罗马数字书写规则,不会出现跨位等情况。
- IL 和 IM 这样的例子并不符合题目要求,49 应该写作 XLIX,999 应该写作 CMXCIX。
- 关于罗马数字的详尽书写规则,可以参考 罗马数字 - 百度百科。
2. 🎯 s.1 - 哈希表(存储所有情况)
c
int romanToInt(char* s) {
// 映射表:包含 7 个单字符 + 6 个特殊双字符
char* keys[] = {"IV", "IX", "XL", "XC", "CD", "CM", "I",
"V", "X", "L", "C", "D", "M"};
int vals[] = {4, 9, 40, 90, 400, 900, 1, 5, 10, 50, 100, 500, 1000};
int n = 13;
int result = 0;
int i = 0;
int len = strlen(s);
while (i < len) {
int matched = 0;
// 先尝试匹配双字符特殊情况
if (i + 1 < len) {
char two[3] = {s[i], s[i + 1], '\0'};
for (int k = 0; k < 6; k++) {
if (strcmp(two, keys[k]) == 0) {
result += vals[k];
i += 2;
matched = 1;
break;
}
}
}
if (!matched) {
// 匹配单字符正常情况
char one[2] = {s[i], '\0'};
for (int k = 6; k < n; k++) {
if (strcmp(one, keys[k]) == 0) {
result += vals[k];
break;
}
}
i++;
}
}
return result;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
js
/**
* @param {string} s
* @return {number}
*/
var romanToInt = function (s) {
const map = new Map()
map.set('I', 1)
map.set('V', 5)
map.set('X', 10)
map.set('L', 50)
map.set('C', 100)
map.set('D', 500)
map.set('M', 1000)
// 特殊值
map.set('IV', 4)
map.set('IX', 9)
map.set('XL', 40)
map.set('XC', 90)
map.set('CD', 400)
map.set('CM', 900)
let result = 0
for (let i = 0; i < s.length; i++) {
if (map.has(`${s[i]}${s[i + 1]}`)) {
// 特殊情况
result += map.get(`${s[i]}${s[i + 1]}`)
i++
} else {
// 非特殊情况
result += map.get(s[i])
}
}
return result
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
py
class Solution:
def romanToInt(self, s: str) -> int:
mapping = {
"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,
}
result = 0
i = 0
while i < len(s):
two = s[i : i + 2]
if two in mapping: # 匹配双字符特殊情况
result += mapping[two]
i += 2
else: # 匹配单字符正常情况
result += mapping[s[i]]
i += 1
return result1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
- 时间复杂度:
,其中 是字符串长度,每个字符最多被访问一次 - 空间复杂度:
,哈希表大小固定为 13
算法思路:
- 预先将 7 个单字符和 6 个双字符特殊情况全部存入哈希表
- 逐位扫描字符串,每次首先尝试匹配当前双字符子串,匹配则累加对应值并跳过 2 个字符,否则匹配单字符并前进 1
3. 🎯 s.2 - 哈希表(仅存储正常情况)
c
int charToVal(char c) {
switch (c) {
case 'I':
return 1;
case 'V':
return 5;
case 'X':
return 10;
case 'L':
return 50;
case 'C':
return 100;
case 'D':
return 500;
case 'M':
return 1000;
default:
return 0;
}
}
int romanToInt(char* s) {
int ans = 0;
int len = strlen(s);
for (int i = 0; i < len; i++) {
int cur = charToVal(s[i]);
int next = (i + 1 < len) ? charToVal(s[i + 1]) : 0;
if (cur < next) {
ans += next - cur; // 特殊情况:取反并与下一个符号合并
i++;
} else {
ans += cur; // 正常情况:直接累加
}
}
return ans;
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
js
/**
* @param {string} s
* @return {number}
*/
var romanToInt = function (s) {
const map = new Map()
map.set('I', 1)
map.set('V', 5)
map.set('X', 10)
map.set('L', 50)
map.set('C', 100)
map.set('D', 500)
map.set('M', 1000)
let ans = 0
for (let i = 0; i < s.length; i++) {
if (map.get(s[i]) < map.get(s[i + 1])) {
// 特殊情况
ans += map.get(s[i + 1]) - map.get(s[i])
i++
} else {
// 正常情况
ans += map.get(s[i])
}
}
return ans
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
py
class Solution:
def romanToInt(self, s: str) -> int:
mapping = {"I": 1, "V": 5, "X": 10, "L": 50, "C": 100, "D": 500, "M": 1000}
ans = 0
for i in range(len(s)):
cur = mapping[s[i]]
next_ = mapping[s[i + 1]] if i + 1 < len(s) else 0
if cur < next_: # 特殊情况:左小右大,取反累加
ans -= cur
else: # 正常情况:直接累加
ans += cur
return ans1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
- 时间复杂度:
,其中 是字符串长度,单次线性遍历 - 空间复杂度:
,哈希表大小固定为 7
算法思路:
- 哈希表只存储 7 个单字符的映射,利用特殊情况的内在规律处理减法叠加形式
- 【正常情况】当前字符对应值
下一字符对应值,直接累加当前字符的值 - 【特殊情况】当前字符对应值
下一字符对应值,将当前字符当作负数累加(减法)