이 글에서는 로마 숫자 문자열을 입력받아 해당하는 십진수(10진법) 값을 반환하는 JavaScript 함수를 작성해 보겠습니다. 로마 숫자는 I(1), V(5), X(10), L(50), C(100), D(500), M(1000)의 일곱 가지 기호로 표현되며, 작은 값의 기호가 큰 값의 기호 앞에 올 경우 뺄셈으로 처리한다는 것이 핵심 규칙입니다. 예를 들어 IV는 4, IX는 9를 의미합니다.
알고리즘 동작 원리
이 알고리즘은 문자열을 왼쪽부터 검사하면서 다음 규칙에 따라 값을 누적합니다.
- 현재 기호가 바로 뒤의 기호보다 값이 작다면(예: IV, IX, XL처럼 앞선 기호가 뒤따르는 기호보다 작은 경우), 두 기호를 하나의 단위로 묶어 '뒤 기호 값 − 앞 기호 값'을 더하고 두 글자를 건너뜁니다.
- 그렇지 않다면 현재 기호의 값을 그대로 더하고 한 글자만 건너뜁니다.
문자열이 모두 소진될 때까지 이 과정을 반복한 후 누적된 합계를 반환하면 됩니다. 시간 복잡도는 문자열 길이에 비례하는 O(n)으로 효율적입니다.
예제 코드
const romanToInt = (s) => {
const legend = "IVXLCDM";
const l = [1, 5, 10, 50, 100, 500, 1000];
let sum = 0;
while (s) {
if (!!s[1] && legend.indexOf(s[0]) < legend.indexOf(s[1])) {
sum += (l[legend.indexOf(s[1])] - l[legend.indexOf(s[0])]);
s = s.substring(2, s.length);
} else {
sum += l[legend.indexOf(s[0])];
s = s.substring(1, s.length);
}
}
return sum;
};
console.log(romanToInt('CLXXVIII'));
console.log(romanToInt('LXXXIX'));
console.log(romanToInt('LV'));
console.log(romanToInt('MDLV'));
실행 결과
위 코드를 실행하면 콘솔에 다음과 같이 출력됩니다.
178 89 55 1555
결과 해석
각 입력값이 어떻게 계산되었는지 살펴보면 다음과 같습니다.
- CLXXVIII → C(100) + L(50) + X(10) + X(10) + V(5) + I(1) + I(1) + I(1) = 178
- LXXXIX → L(50) + X(10) + X(10) + IX(10−1) = 89
- LV → L(50) + V(5) = 55
- MDLV → M(1000) + D(500) + L(50) + V(5) = 1555
이처럼 기호별 값을 배열로 매핑해 두고 인접한 두 기호의 대소 관계만 비교하면, 뺄셈 규칙이 포함된 로마 숫자도 간단하게 십진수로 변환할 수 있습니다.