かずとよのアウトプット日記

開発エンジニアを目指しています。

leetcode : Roman to Integer

こんにちは。かずとよです。

本日も、leetcodeです。

早速問題を。

Roman numerals are represented by seven different symbols: IVXLCD and M.

Symbol       Value
I             1
V             5
X             10
L             50
C             100
D             500
M             1000

For example, 2 is written as II in Roman numeral, just two one's added together. 12 is written as XII, which is simply X + II. The number 27 is written as XXVII, which is XX + V + II.

Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used:

  • I can be placed before V (5) and X (10) to make 4 and 9. 
  • X can be placed before L (50) and C (100) to make 40 and 90. 
  • C can be placed before D (500) and M (1000) to make 400 and 900.

Given a roman numeral, convert it to an integer.

 

Example 1:

Input: s = "III"
Output: 3

Example 2:

Input: s = "IV"
Output: 4

Example 3:

Input: s = "IX"
Output: 9

Example 4:

Input: s = "LVIII"
Output: 58
Explanation: L = 50, V= 5, III = 3.

Example 5:

Input: s = "MCMXCIV"
Output: 1994
Explanation: M = 1000, CM = 900, XC = 90 and IV = 4.

 

Constraints:

  • 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].

 

めっちゃ長い。頑張って解読。

【ローマ数字では、I,V,X,L,C,D,Mの7つの異なる記号で数字を表します。I = 1, V = 5, X = 10, L = 50, C = 100, D = 500, M = 1000

(なんやかんや、この表し方の説明が続くので、省略)

ローマ数字を与えられたら、それを整数に変換してね】

みたいな感じだと思います。

長くなりそうだったので省略してしまいましたが、VI = 6とかIV = 4とかの説明や、それ以外のシンボルでも考え方は同じだよって書いてるみたいです。

 

まず、このシンボル。見たことあります。ファイナルファンタジーとかで。笑

でも、大体のは50以上になることないので、L以上のシンボルは初見でした。一つ学べました。

あと、このシンボルのIVとかVIの考え方も、今までなんとなくで理解してたところが理由込みで理解できたのでスッキリしました。

 

さて、色々学べて理解も進んだところで、早速解きます!!

 

試行錯誤します。かなり試行錯誤しました。

で、回答が以下。

 

def roman_to_int(s)
  s_ary = s.split(//)
           # とりあえず渡された値を分割して配列へ
  answer = 0
           # あとあとの回答のために変数定義

# -----------------------①

  s_ary.each_with_index do |s, idx|
           # 先ほど作った配列を繰り返し処理(インデックスも)
    if idx != 0
           # 配列の頭じゃなければ以下を処理
      if s == "V" && s_ary[idx - 1] == "I" || s == "X" && s_ary[idx - 1] == "I"
           # 要素がVで且つ、一つ前の要素がI。もしくは、要素がXで且つ、一つ前の要素がIなら
        s_ary[idx - 1] = "-I"
           # 一つ前の要素を-Iと置き換え
      elsif s == "L" && s_ary[idx - 1] == "X" || s == "C" && s_ary[idx - 1] == "X"
           # 要素がLで且つ、一つ前の要素がX。もしくは、要素がCで且つ、一つ前の要素がXなら
        s_ary[idx - 1] = "-X"
           # 一つ前の要素を-Xと置き換え
      elsif s == "D" && s_ary[idx - 1] == "C" || s == "M" && s_ary[idx - 1] == "C"
           # 要素がDで且つ、一つ前の要素がC。もしくは、要素がMで且つ、一つ前の要素がCなら
        s_ary[idx - 1] = "-C"
           # 一つ前の要素を-Cと置き換え
      end
    end
  end
# -----------------------①
# -----------------------② s_ary.each do |i| # 再度、配列の中身を繰り返し if i == "-I" # もし要素が-Iなら answer -= 1 # 答えから-1 elsif i == "-X" # もし要素が-Xなら answer -= 10 # 答えから-10 elsif i == "-C" # もし要素が-Cなら answer -= 100 # 答えから-100 else # それ以外(要素が-のないものなら) i.sub!(/I/, "1") i.sub!(/V/, "5") i.sub!(/X/, "10") i.sub!(/L/, "50") i.sub!(/C/, "100") i.sub!(/D/, "500") i.sub!(/M/, "1000") # 要素を数字(文字列で)に置き換え answer += i.to_i # 答えに先程の要素を数値に変換して足す end end

# -----------------------②
return answer end

 

一応これでクリアにはなりましたが・・・めちゃ長い。

この回答が、僕の持ってる知識と、調べられる限界でした。

 

あまりにも長くなったので、自分のために解説しておきます。

 

とりあえず、渡されたシンボルは一つずつ分割して配列に入れました。

それで、①について。

①では、この配列の中から、4や9、40や90などといった、マイナスの処理をしないといけないものを探し出して、マイナスとわかる形に置き換えました。

これ、最初は2個目のif文だけでやってました。

ただそうすると、頭にVやXなどがあった時、インデックスが-1になってしまって、配列の最後を見てしまい、意図しないところが置き換えられてしまうという事が起きたため、1個目のif文で囲みました。

 

次に②。②では、①で置き換えた配列の要素たちを合計するだけ。

ただ、配列の中身はまだシンボルのままだし文字列だし、文字列から数値に変換するとマイナスが消えちゃうし・・・っていうことで、if文で分岐させながら、答えとなる変数に足したり引いたりしていきました。

 

うーん・・・一応クリアできたものの、こんなんでいいのか?

ほんまにむずかしいです。

 

*今回の学び:

  • romanは、浪漫のことじゃない。笑
  • V、Xの次にもシンボルがあったという事。実生活では使わないので知らなかった。
  • VやXの前にあるIはマイナス。後にあるのはプラス。L、C、D、M等でも同じ考え方。
  • splitメソッドで、文字列を分割して配列に格納できる。引数に区切る文字などを入れて使うと、任意の場所で分割できる。今回は、//を引数とし、一文字ずつ分割して使った。
  • with_indexめちゃ便利
  • subメソッドでは、文字列の中の指定した部分を置き換えできる。
    今回は、置き換えた後に使いたかったので、元となるオブジェクト自体を変更できる破壊的メソッドsub!を使いました。