Roman to Integer
Convert a Roman numeral string to its integer value.
- 1 <= s.length <= 15
- s contains only the characters ('I', 'V', 'X', 'L', 'C', 'D', 'M').
- It is guaranteed that s is a valid roman numeral in the range [1, 3999].
Intuition
Roman to integer converts a Roman numeral to its value. Roman notation is mostly additive — symbols run largest to smallest and sum — with six subtractive exceptions like IV for 4 and CM for 900.
Handling those six as special cases produces long conditional chains. The observation that removes them entirely:
- A symbol is subtracted exactly when it is smaller than the symbol immediately after it, and added otherwise.
In IV, the I precedes a larger V, so it subtracts. In VI, the I follows and adds. That one comparison covers all six subtractive pairs without naming any of them.
So scan left to right, comparing each symbol with its successor, and add or subtract accordingly. The final symbol has no successor and is always added.
An equivalent formulation scans right to left, adding when a symbol is at least as large as the previous one seen and subtracting otherwise. Some find this cleaner since it avoids looking ahead.
A lookup table maps the seven symbols to their values, and the input is guaranteed valid, so no verification of malformed numerals is needed.
The comparison must be strictly less than. Using <= would wrongly subtract in cases like II, where two equal symbols should both add.
The constraints cap the input at 3999, so no symbol above M appears.
Integer to Roman is the reverse direction, where the subtractive forms are placed in the lookup table as values in their own right — a neat symmetry with this problem's approach of not naming them at all.
One pass gives O(n) time and O(1) space.
Roman numerals are additive except when a smaller symbol precedes a larger one, which signals subtraction. So a single left-to-right pass that compares each character to its neighbour handles every case — no lookup table of the six subtractive pairs needed.
Approach
Before reading on: there are only six subtractive pairs, but special-casing each is fragile. Compare a symbol with the one after it and a single rule falls out — what is it?
Find the single rule
A symbol subtracts exactly when it is smaller than the one after it, and adds otherwise. This covers all six subtractive pairs without naming any.
Map symbols to values
A seven-entry lookup gives each symbol's value. The input is guaranteed valid, so no malformed-numeral checking is needed.
Compare with the successor
Scan left to right, testing each symbol against the next. The final symbol has no successor and is always added.
Use strict comparison
Test with <, not <=. Using <= wrongly subtracts in II, where two equal symbols should both add.
Or scan right to left
Add when a symbol is at least as large as the previous one seen, subtract otherwise. Equivalent, and avoids looking ahead — some find it cleaner.
Rely on the input bound
Values are capped at 3999, so no symbol above M occurs and no overline notation needs handling.
Cost of the conversion
One pass with constant work per symbol gives O(n) time and O(1) space for the fixed lookup table.
Solution & live demo
Common pitfalls
Hardcoding the subtractive pairs
s = s.replace('IV', '4').replace('IX', '9')...if i + 1 < len(s) and val[ch] < val[s[i + 1]]:
total -= val[ch]Six special cases is six chances to typo, and the substitution approach breaks if the replacement characters collide with real numerals. The comparison rule generates all six from one principle.
Reading past the end on the last character
if val[ch] < val[s[i + 1]]:
if i + 1 < len(s) and val[ch] < val[s[i + 1]]:
The final symbol has no successor, so indexing i + 1 throws. It can never be subtractive anyway — there's nothing after it to be larger.
Using <= for the comparison
if val[ch] <= val[s[i + 1]]:
if val[ch] < val[s[i + 1]]:
Equal symbols are additive: "II" is 2, not 0. Only a strictly smaller symbol before a larger one indicates subtraction.
Edge cases
I (1) is less than X (10) so it subtracts; X has no successor so it adds — total 9.
No successor exists, so it is simply added.
Each I has an equal (not greater) successor, so all are added — total 3.