Skip to content

Commit 8b827a4

Browse files
authored
Merge pull request #5 from borrelunde/integer_to_roman
LeetCode problem: 12. Integer to Roman
2 parents fdf4bc0 + 8db22c1 commit 8b827a4

File tree

3 files changed

+319
-0
lines changed

3 files changed

+319
-0
lines changed
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# 12. Integer to Roman
2+
3+
Difficulty: `Medium`
4+
Topics: `Hash Table`, `Math`, `String`
5+
6+
Roman numerals are represented by seven different symbols: `I`, `V`, `X`, `L`, `C`, `D` and `M`.
7+
8+
```text
9+
Symbol Value
10+
I 1
11+
V 5
12+
X 10
13+
L 50
14+
C 100
15+
D 500
16+
M 1000
17+
```
18+
19+
For example, `2` is written as `II` in Roman numeral, just two ones added together. `12` is written as `XII`, which is
20+
simply `X + II`. The number `27` is written as `XXVII`, which is `XX + V + II`.
21+
22+
Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not `IIII`.
23+
Instead, the number four is written as `IV`. Because the one is before the five we subtract it making four. The same
24+
principle applies to the number nine, which is written as `IX`. There are six instances where subtraction is used:
25+
26+
- `I` can be placed before `V` (5) and `X` (10) to make 4 and 9.
27+
- `X` can be placed before `L` (50) and `C` (100) to make 40 and 90.
28+
- `C` can be placed before `D` (500) and `M` (1000) to make 400 and 900.
29+
30+
Given an integer, convert it to a roman numeral.
31+
32+
**Example 1:**
33+
34+
```text
35+
Input: num = 3
36+
Output: "III"
37+
Explanation: 3 is represented as 3 ones.
38+
```
39+
40+
**Example 2:**
41+
42+
```text
43+
Input: num = 58
44+
Output: "LVIII"
45+
Explanation: L = 50, V = 5, III = 3.
46+
```
47+
48+
**Example 3:**
49+
50+
```text
51+
Input: num = 1994
52+
Output: "MCMXCIV"
53+
Explanation: M = 1000, CM = 900, XC = 90 and IV = 4.
54+
```
55+
56+
**Constraints:**
57+
58+
- `1 <= num <= 3999`
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package com.bl.integer_to_roman;
2+
3+
/**
4+
* This is the solution to the LeetCode problem: 12. Integer to Roman
5+
*
6+
* @author Børre A. Opedal Lunde
7+
* @since 2024.01.27
8+
*/
9+
public class Solution {
10+
11+
// This class stores the Roman numeral and its corresponding integer value.
12+
// (First I used a map, but the solution became really clumsy.)
13+
private static class RomanNumeral {
14+
15+
private final int value;
16+
private final String numeral;
17+
18+
public RomanNumeral(final int number, final String symbol) {
19+
this.value = number;
20+
this.numeral = symbol;
21+
}
22+
23+
// No getters are needed because it's a private inner class.
24+
}
25+
26+
// The Roman numerals are sorted in descending order because we want to
27+
// start with the largest and subtract our way down.
28+
private static final RomanNumeral[] ROMAN_NUMERALS = {
29+
new RomanNumeral(1000, "M"),
30+
new RomanNumeral(900, "CM"),
31+
new RomanNumeral(500, "D"),
32+
new RomanNumeral(400, "CD"),
33+
new RomanNumeral(100, "C"),
34+
new RomanNumeral(90, "XC"),
35+
new RomanNumeral(50, "L"),
36+
new RomanNumeral(40, "XL"),
37+
new RomanNumeral(10, "X"),
38+
new RomanNumeral(9, "IX"),
39+
new RomanNumeral(5, "V"),
40+
new RomanNumeral(4, "IV"),
41+
new RomanNumeral(1, "I")
42+
};
43+
44+
public String intToRoman(int num) {
45+
46+
// It is faster to use a string builder than a string.
47+
final StringBuilder builder = new StringBuilder();
48+
49+
// Go through every Roman numeral and subtract our way down, while
50+
// appending the Roman numeral to the string builder.
51+
for (final RomanNumeral romanNumeral : ROMAN_NUMERALS) {
52+
while (num >= romanNumeral.value) {
53+
num -= romanNumeral.value;
54+
builder.append(romanNumeral.numeral);
55+
}
56+
}
57+
return builder.toString();
58+
}
59+
}
Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
package com.bl.integer_to_roman;
2+
3+
import org.junit.jupiter.api.DisplayName;
4+
import org.junit.jupiter.api.Test;
5+
import org.junit.jupiter.params.ParameterizedTest;
6+
import org.junit.jupiter.params.provider.Arguments;
7+
import org.junit.jupiter.params.provider.MethodSource;
8+
9+
import java.util.stream.Stream;
10+
11+
import static org.junit.jupiter.api.Assertions.assertEquals;
12+
13+
/**
14+
* This is the test to the LeetCode problem: 12. Integer to Roman
15+
*
16+
* @author Børre A. Opedal Lunde
17+
* @since 2024.01.27
18+
*/
19+
@DisplayName("Integer to Roman")
20+
class SolutionTest {
21+
22+
final Solution solution = new Solution();
23+
24+
@Test
25+
@DisplayName("Example one")
26+
void exampleOne() {
27+
int num = 3;
28+
String expected = "III";
29+
String actual = solution.intToRoman(num);
30+
assertEquals(expected, actual);
31+
}
32+
33+
@Test
34+
@DisplayName("Example two")
35+
void exampleTwo() {
36+
int num = 58;
37+
String expected = "LVIII";
38+
String actual = solution.intToRoman(num);
39+
assertEquals(expected, actual);
40+
}
41+
42+
@Test
43+
@DisplayName("Example three")
44+
void exampleThree() {
45+
int num = 1994;
46+
String expected = "MCMXCIV";
47+
String actual = solution.intToRoman(num);
48+
assertEquals(expected, actual);
49+
}
50+
51+
static Stream<Arguments> providePrincipalNumbers() {
52+
return Stream.of(
53+
Arguments.of(1, "I"),
54+
Arguments.of(5, "V"),
55+
Arguments.of(10, "X"),
56+
Arguments.of(50, "L"),
57+
Arguments.of(100, "C"),
58+
Arguments.of(500, "D"),
59+
Arguments.of(1000, "M")
60+
);
61+
}
62+
63+
@ParameterizedTest(name = "Number {0} is Roman numeral {1}")
64+
@DisplayName("Principal Roman numbers")
65+
@MethodSource("providePrincipalNumbers")
66+
void principalRomanNumerals(int num, String expected) {
67+
String actual = solution.intToRoman(num);
68+
assertEquals(expected, actual);
69+
}
70+
71+
static Stream<Arguments> provideSubtractiveNumbers() {
72+
return Stream.of(
73+
Arguments.of(4, "IV"),
74+
Arguments.of(9, "IX"),
75+
Arguments.of(40, "XL"),
76+
Arguments.of(90, "XC"),
77+
Arguments.of(400, "CD"),
78+
Arguments.of(900, "CM")
79+
);
80+
}
81+
82+
@ParameterizedTest(name = "Number {0} is Roman numeral {1}")
83+
@DisplayName("Subtractive Roman numbers")
84+
@MethodSource("provideSubtractiveNumbers")
85+
void subtractiveRomanNumerals(int num, String expected) {
86+
String actual = solution.intToRoman(num);
87+
assertEquals(expected, actual);
88+
}
89+
90+
static Stream<Arguments> provideNumbersFromOneToOneHundred() {
91+
return Stream.of(
92+
Arguments.of(1, "I"),
93+
Arguments.of(2, "II"),
94+
Arguments.of(3, "III"),
95+
Arguments.of(4, "IV"),
96+
Arguments.of(5, "V"),
97+
Arguments.of(6, "VI"),
98+
Arguments.of(7, "VII"),
99+
Arguments.of(8, "VIII"),
100+
Arguments.of(9, "IX"),
101+
Arguments.of(10, "X"),
102+
Arguments.of(11, "XI"),
103+
Arguments.of(12, "XII"),
104+
Arguments.of(13, "XIII"),
105+
Arguments.of(14, "XIV"),
106+
Arguments.of(15, "XV"),
107+
Arguments.of(16, "XVI"),
108+
Arguments.of(17, "XVII"),
109+
Arguments.of(18, "XVIII"),
110+
Arguments.of(19, "XIX"),
111+
Arguments.of(20, "XX"),
112+
Arguments.of(21, "XXI"),
113+
Arguments.of(22, "XXII"),
114+
Arguments.of(23, "XXIII"),
115+
Arguments.of(24, "XXIV"),
116+
Arguments.of(25, "XXV"),
117+
Arguments.of(26, "XXVI"),
118+
Arguments.of(27, "XXVII"),
119+
Arguments.of(28, "XXVIII"),
120+
Arguments.of(29, "XXIX"),
121+
Arguments.of(30, "XXX"),
122+
Arguments.of(31, "XXXI"),
123+
Arguments.of(32, "XXXII"),
124+
Arguments.of(33, "XXXIII"),
125+
Arguments.of(34, "XXXIV"),
126+
Arguments.of(35, "XXXV"),
127+
Arguments.of(36, "XXXVI"),
128+
Arguments.of(37, "XXXVII"),
129+
Arguments.of(38, "XXXVIII"),
130+
Arguments.of(39, "XXXIX"),
131+
Arguments.of(40, "XL"),
132+
Arguments.of(41, "XLI"),
133+
Arguments.of(42, "XLII"),
134+
Arguments.of(43, "XLIII"),
135+
Arguments.of(44, "XLIV"),
136+
Arguments.of(45, "XLV"),
137+
Arguments.of(46, "XLVI"),
138+
Arguments.of(47, "XLVII"),
139+
Arguments.of(48, "XLVIII"),
140+
Arguments.of(49, "XLIX"),
141+
Arguments.of(50, "L"),
142+
Arguments.of(51, "LI"),
143+
Arguments.of(52, "LII"),
144+
Arguments.of(53, "LIII"),
145+
Arguments.of(54, "LIV"),
146+
Arguments.of(55, "LV"),
147+
Arguments.of(56, "LVI"),
148+
Arguments.of(57, "LVII"),
149+
Arguments.of(58, "LVIII"),
150+
Arguments.of(59, "LIX"),
151+
Arguments.of(60, "LX"),
152+
Arguments.of(61, "LXI"),
153+
Arguments.of(62, "LXII"),
154+
Arguments.of(63, "LXIII"),
155+
Arguments.of(64, "LXIV"),
156+
Arguments.of(65, "LXV"),
157+
Arguments.of(66, "LXVI"),
158+
Arguments.of(67, "LXVII"),
159+
Arguments.of(68, "LXVIII"),
160+
Arguments.of(69, "LXIX"),
161+
Arguments.of(70, "LXX"),
162+
Arguments.of(71, "LXXI"),
163+
Arguments.of(72, "LXXII"),
164+
Arguments.of(73, "LXXIII"),
165+
Arguments.of(74, "LXXIV"),
166+
Arguments.of(75, "LXXV"),
167+
Arguments.of(76, "LXXVI"),
168+
Arguments.of(77, "LXXVII"),
169+
Arguments.of(78, "LXXVIII"),
170+
Arguments.of(79, "LXXIX"),
171+
Arguments.of(80, "LXXX"),
172+
Arguments.of(81, "LXXXI"),
173+
Arguments.of(82, "LXXXII"),
174+
Arguments.of(83, "LXXXIII"),
175+
Arguments.of(84, "LXXXIV"),
176+
Arguments.of(85, "LXXXV"),
177+
Arguments.of(86, "LXXXVI"),
178+
Arguments.of(87, "LXXXVII"),
179+
Arguments.of(88, "LXXXVIII"),
180+
Arguments.of(89, "LXXXIX"),
181+
Arguments.of(90, "XC"),
182+
Arguments.of(91, "XCI"),
183+
Arguments.of(92, "XCII"),
184+
Arguments.of(93, "XCIII"),
185+
Arguments.of(94, "XCIV"),
186+
Arguments.of(95, "XCV"),
187+
Arguments.of(96, "XCVI"),
188+
Arguments.of(97, "XCVII"),
189+
Arguments.of(98, "XCVIII"),
190+
Arguments.of(99, "XCIX"),
191+
Arguments.of(100, "C")
192+
);
193+
}
194+
195+
@ParameterizedTest(name = "Number {0} is Roman numeral {1}")
196+
@DisplayName("Roman numerals from one to one hundred")
197+
@MethodSource("provideNumbersFromOneToOneHundred")
198+
void romanNumeralsFromOneToOneHundred(int num, String expected) {
199+
String actual = solution.intToRoman(num);
200+
assertEquals(actual, expected);
201+
}
202+
}

0 commit comments

Comments
 (0)