Roman to Integer
Roman numerals use seven symbols: I = 1, V = 5, X = 10, L = 50, C = 100, D = 500 and M = 1000. Normally symbols are written from largest to smallest and their values are added, so XVII is 17.
Six pairs break that order and mean a subtraction instead: IV (4), IX (9), XL (40), XC (90), CD (400) and CM (900).
Given a valid Roman numeral s, return the integer it represents.
Example 1
Example 2
Example 3
Constraints
1 <= s.length <= 15scontains only the charactersI V X L C D M.sis a valid Roman numeral for a value between 1 and 3999.
Pattern Path · Math, Bits & Strings · problem 28 of 169
Start simple
Matching the six two-letter pairs first and then single letters also runs in O(n); there is no meaningfully slower naive approach here.
The key idea
Every subtractive pair has the same shape: a smaller symbol placed directly before a larger one. In a valid numeral that is the only time a smaller symbol precedes a larger one. So we do not need to list the six special pairs at all.
Walk through the string. For each symbol, look at the next one. If the current value is smaller than the next value, the current symbol is the "minus" half of a subtractive pair, so subtract it. Otherwise add it. The last symbol has no neighbour and is always added.
For example, in CMXCIX: C < M so subtract 100, add 1000, X < C so subtract 10, add 100, I < X so subtract 1, add 10, giving 999. Treating CM as -100 + 1000 is exactly the same as adding 900, which is why the rule works.
Complexity
- Time:
O(n) - Space:
O(1)
Python
VALUES = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, 'M': 1000}
class Solution:
def romanToInt(self, s: str) -> int:
total = 0
for i, ch in enumerate(s):
value = VALUES[ch]
# a smaller symbol before a larger one is subtracted (IV, XC, ...)
if i + 1 < len(s) and value < VALUES[s[i + 1]]:
total -= value
else:
total += value
return total
JavaScript
const VALUES = { I: 1, V: 5, X: 10, L: 50, C: 100, D: 500, M: 1000 };
var romanToInt = function(s) {
let total = 0;
for (let i = 0; i < s.length; i++) {
const value = VALUES[s[i]];
// a smaller symbol before a larger one is subtracted (IV, XC, ...)
if (i + 1 < s.length && value < VALUES[s[i + 1]]) total -= value;
else total += value;
}
return total;
};
What the interviewer is checking
It tests whether you can spot a simple local rule (compare with the next symbol) instead of hard-coding every special case.
Mistakes to avoid
- Reading past the end of the string when looking at the next symbol.
- Hard-coding only some of the six subtractive pairs.
- Subtracting the larger symbol instead of the smaller one.
Follow-ups to expect
- Convert an integer back to a Roman numeral. (Greedy over value-symbol table.)
- How would you validate that s is a well-formed numeral? (Check repeat and ordering rules.)
- Can you scan right to left instead? (Subtract when smaller than the running maximum.)
In real software
Parsing chapter numbers, copyright years or outline labels in document-processing tools uses this exact conversion, and the "look at the next token" rule is a tiny example of lookahead in parsers.