이 문제는 로마 숫자를 아라비아 숫자로 변환하는 문제이다.
로마 숫자에는 I, V, X, L, C, D, M가 존재하며 각각 1, 5, 10, 50, 100, 500, 1000을 뜻한다. 하지만 두 문자를 조합하여 다른 숫자를 만들 수 있으니 한 문자씩 변환하면 안된다. 두 번째 문자부터 확인하면서 이전 문자와의 조합을 확인하면 된다. 조합이 가능한 문자이면 두 문자를 합쳐서 아라비아 숫자로 변환하고 조합이 안되면 이전 숫자만 아라비아 숫자로 변환하면 된다.
예를 들어 IVXI가 있다고 해보자. 두 번째 문자는 V이다. 이전 문자는 I로 IV는 조합이 가능한 경우이다. 그렇기 때문에 IV를 4로 변환한다. 문자 2개가 변환되었기 때문에 2개를 뛰어서 4번째 문자인 I를 탐색한다. I는 X와 조합이 불가능하기 때문에 X만 10으로 변환한다. 하나의 문자만 변환이 가능하기 때문에 바로 1개를 뛰어넘어서 5번째 문자를 확인한다. (이러한 경우을 위해 탐색을 할때는 마지막에 I를 하나 더 붙여서 탐색을 하고 마지막에 1을 빼준다.) 5번째 문자 I는 이전 문자 I와 조합이 불가능하기 때문에 이전 문자만 1로 변환한다. 이를 다시 순차적으로 보면 IVXI에 I를 붙여서 IVXII로 만들고 IV를 4로 변환하고 X를 10으로 변환하고 I 2개를 2로 변환한다. 그러면 총 16이 되고 처음에 붙인 I를 제외하여 답은 15가 된다.
fun romanToInt(s: String): Int {
val str = s + "I"
var it = 1
var sum = 0
while (it < str.length) {
sum += when (str[it]) {
'I' -> convert(str[it++ - 1])
else -> if (str[it - 1] == map(str[it])) {
it += 2
convert(str[it - 2]) - convert(str[it - 3])
} else convert(str[it++ - 1])
}
}
return sum
}
private fun convert(ch: Char): Int = when (ch) {
'I' -> 1
'V' -> 5
'X' -> 10
'L' -> 50
'C' -> 100
'D' -> 500
'M' -> 1000
else -> 0
}
private fun map(ch: Char): Char = when (ch) {
'V', 'X' -> 'I'
'L', 'C' -> 'X'
'D', 'M' -> 'C'
else -> '\u0000'
}