Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions 8/step1.md
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
```
55 changes: 55 additions & 0 deletions 8/step2.md
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
```
34 changes: 34 additions & 0 deletions 8/step3.md
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

Copy link
Copy Markdown

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を行う処理に変えてもよいと思いました。

end

max_value = 2 ** 31 - 1
min_value = (-1) * 2 ** 31

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ある種の定数なので、大文字で定義してもいいかもしれないと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

コメントありがとうございます!
Rubyはメソッドの外側でしか定数を定義できないので変数にしています。
メソッドの外に出してしまうと定数としては定義できますがスコープが広くなるので悩ましいですね…

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"-2147483648" (min_value) が入力されたら Rounding の対象ではないが、この行で return されるように思いました。正しい値を返せていますが、少し引っかかりました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ありがとうございます。
確かに改めて見るとMIN_VALUEなのにオーバーフロー扱いしているように見えるので気持ち悪いですね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The 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

こっちの方が良いかもしれません

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

絶対値の上限を正負で分けたということですね。自分だったら absolute_value_limit のようにします。
改善案がなく恐縮ですが、2 ** 31 とハードコーディングしている箇所が増えたのは保守性が落ちてしまっているかもしれないと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

absolute_value_limit = (sign == 1) ? max_value : (-1) * min_value

とするとかですかね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

確かに Ruby だとそれで良いですね。

end

result = result * 10 + digit
index += 1
end
result * sign
end
```
1 change: 1 addition & 0 deletions 8/step4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
## step4 レビューを受けて解答を修正