-
Notifications
You must be signed in to change notification settings - Fork 0
8. String to Integer (atoi) #51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| # step1 何も見ずに解く | ||
|
|
||
| 正規表現で数値に対応する部分を取ってきてintegerに変換する方法を考えた。 | ||
| Rubyでは64-bit以上の数値も扱うことができるのでstring to intに変換した時にoverflowのエラーが起きることはないが、 | ||
| 他の言語だとoverflolwエラーを捕捉して最大値、最小値に変換するのが良いのだろうか。 | ||
| ただ、例外処理はスタックトレースの生成などオーバヘッドが大きいので桁数が一定以上を超えた場合は上限、下限の値を早期に返す、という工夫をした方が良さそう。 | ||
|
|
||
| 時間計算量はO(N) | ||
| 空間計算量はO(N) | ||
| Nの最大値は200なので1秒以内に間に合う | ||
|
|
||
| ```ruby | ||
| # @param {String} str | ||
| # @return {Integer} | ||
| def my_atoi(str) | ||
| numeric_string = str.match(/^\s*(\+|-)?([0-9]+)/) | ||
| return 0 if numeric_string.nil? | ||
|
|
||
| sign_string = numeric_string.captures[0] || "+" | ||
| value_string = numeric_string.captures[1] || "0" | ||
|
|
||
| number_of_digits = 0 | ||
| value_string.size.times do |i| | ||
| next if value_string[i] == "0" | ||
|
|
||
| number_of_digits = value_string.size - i | ||
| break | ||
| end | ||
|
|
||
| if number_of_digits > 10 | ||
| if sign_string == "+" | ||
| return 2 ** 31 - 1 | ||
| else | ||
| return (-1) * 2 ** 31 | ||
| end | ||
| end | ||
|
|
||
| if sign_string == "+" | ||
| return [2 ** 31 - 1, value_string.to_i].min | ||
| else | ||
| return [(-1) * 2 ** 31, - value_string.to_i].max | ||
| end | ||
| end | ||
| ``` | ||
|
|
||
| 解き終わった後にRubyのメソッドを調べたところ、String#[]でキャプチャできることを知った。 | ||
| また、`Comparable#clamp`という範囲内の値を返すメソッドがあったので以下のように書ける。 | ||
|
|
||
| ```ruby | ||
| # @param {String} str | ||
| # @return {Integer} | ||
| def my_atoi(str) | ||
| num = str.lstrip[/^[\+\-]?\d+/].to_i | ||
| num.clamp(-2**31, 2**31 - 1) | ||
| end | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,55 @@ | ||
| # step2 他の方の解答を見る | ||
| - https://github.com/katsukii/leetcode/pull/9 | ||
|
|
||
| 例外で処理しなくても確かにこの書き方で対応できる。 | ||
|
|
||
| ```java | ||
| while (index < length && Character.isDigit(s.charAt(index))) { | ||
| int digit = s.charAt(index) - '0'; | ||
|
|
||
| // Check for overflow before adding the digit | ||
| if (result > (Integer.MAX_VALUE - digit) / 10) { | ||
| return sign == 1 ? Integer.MAX_VALUE : Integer.MIN_VALUE; | ||
| } | ||
| result = result * 10 + digit; | ||
| index++; | ||
| } | ||
| ``` | ||
|
|
||
| ```ruby | ||
| # @param {String} str | ||
| # @return {Integer} | ||
| def my_atoi(str) | ||
| index = 0 | ||
|
|
||
| # skip white space | ||
| str = str.lstrip | ||
|
|
||
| # check sign | ||
| sign = 1 | ||
| if str.start_with?("-") | ||
| sign = -1 | ||
| index += 1 | ||
| end | ||
| if str.start_with?("+") | ||
| index += 1 | ||
| end | ||
|
|
||
| # build result | ||
| max_value = 2 ** 31 - 1 | ||
| min_value = (-1) * 2 ** 31 | ||
| result = 0 | ||
| is_integer = ->(string) { string.bytes.first.between?("0".bytes.first, "9".bytes.first )} | ||
| while index < str.size && is_integer.call(str[index]) | ||
| digit = str[index].to_i | ||
|
|
||
| if result > (max_value - digit) / 10 | ||
| return sign == 1 ? max_value : min_value | ||
| end | ||
|
|
||
| result = result * 10 + digit | ||
| index += 1 | ||
| end | ||
| result * sign | ||
| end | ||
| ``` |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,34 @@ | ||
| # step3 3回続けて10分以内に書いてエラーを出さなければOKとする | ||
|
|
||
| ```ruby | ||
| # @param {String} str | ||
| # @return {Integer} | ||
| def my_atoi(str) | ||
| str_without_left_space = str.lstrip | ||
|
|
||
| index = 0 | ||
| sign = 1 | ||
| if str_without_left_space.start_with?("-") | ||
| sign = -1 | ||
| index += 1 | ||
| end | ||
| if str_without_left_space.start_with?("+") | ||
| index += 1 | ||
| end | ||
|
|
||
| max_value = 2 ** 31 - 1 | ||
| min_value = (-1) * 2 ** 31 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ある種の定数なので、大文字で定義してもいいかもしれないと思いました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. コメントありがとうございます! |
||
| result = 0 | ||
| while index < str_without_left_space.size && str_without_left_space[index].between?("0", "9") | ||
| digit = str_without_left_space[index].to_i | ||
|
|
||
| if result > (max_value - digit) / 10 | ||
| return sign == 1 ? max_value : min_value | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "-2147483648" (min_value) が入力されたら Rounding の対象ではないが、この行で return されるように思いました。正しい値を返せていますが、少し引っかかりました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ありがとうございます。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. # @param {String} str
# @return {Integer}
def my_atoi(str)
str_without_left_space = str.lstrip
index = 0
sign = 1
if str_without_left_space.start_with?("-")
sign = -1
index += 1
end
if str_without_left_space.start_with?("+")
index += 1
end
max_value = 2 ** 31 - 1
min_value = (-1) * 2 ** 31
limit = (sign == 1) ? max_value : 2 ** 31
result = 0
while index < str_without_left_space.size && str_without_left_space[index].between?("0", "9")
digit = str_without_left_space[index].to_i
if result > (limit - digit) / 10
return sign == 1 ? max_value : min_value
end
result = result * 10 + digit
index += 1
end
result * sign
endこっちの方が良いかもしれません There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 絶対値の上限を正負で分けたということですね。自分だったら absolute_value_limit のようにします。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. とするとかですかね。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 確かに Ruby だとそれで良いですね。 |
||
| end | ||
|
|
||
| result = result * 10 + digit | ||
| index += 1 | ||
| end | ||
| result * sign | ||
| end | ||
| ``` | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1 @@ | ||
| ## step4 レビューを受けて解答を修正 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
indexを進める代わりに、新しい文字列の変数を定義して、最後のwhile文をその文字列でfor_each_charを行う処理に変えてもよいと思いました。