Skip to content

Conversation

@Kaichi-Irie
Copy link
Owner

問題へのリンク

8. String to Integer (atoi)

言語

Python

自分の解法

dislikesが多いのは、LeetCodeらしからぬ細かい仕様が多いから?

step1

class Solution:
    CHAR_TO_DIGIT = {str(i): i for i in range(10)}

    def myAtoi(self, s: str) -> int:
        SPACE = " "
        PLUS = "+"
        MINUS = "-"
        MIN = -(2**31)
        MAX = 2**31 - 1
        if not s:
            return 0
        num = 0
        # remove leading whitespaces
        i = 0
        while i < len(s) and s[i] == SPACE:
            i += 1
        s = s[i:]
        if not s:
            return 0
        # process sign
        is_negative = False
        if s[0] == PLUS:
            s = s[1:]
        elif s[0] == MINUS:
            s = s[1:]
            is_negative = True
        if not s:
            return 0
        # conversion
        digits: list[int] = []
        for char in s:
            if char not in self.CHAR_TO_DIGIT:
                break
            digit = self.CHAR_TO_DIGIT[char]
            digits.append(digit)
        digits.reverse()
        for i, digit in enumerate(digits):
            num += digit * pow(10, i)
        num = -num if is_negative else num
        # rounding
        num = min(num, MAX)
        num = max(num, MIN)

        return num
  • leading whitespace を取り除く箇所で何度かミスってしまった

    • まずs.replace(" ", "")で全ての空白を取り除いてしまっていた
    • 次に前から空白を取り除く処理を実装したが、何度かバグを踏んだ
    • while i < len(s) and ...と先にandしておくと、ilen(s)に達したときにs[i]でIndexErrorになる問題を防げる
  • 時間計算量:O(len(s))

  • 空間計算量:O(1)

Tests

  • standard cases:
    • s=" 00321a3" -> 321
    • s=" +00321a3" -> 321
    • s=" -00321a3" -> -321
  • empty:
    • s="" -> 0
  • invalid cases:
    • s=" " -> 0
    • s="a" -> 0
    • s="a1" -> 0
  • whitespaces:
    • s=" 1" -> 1
  • signedness:
    • s="1" -> 1
    • s="+1" -> 1
    • s="-1" -> -1
  • leading zeros:
    • s="0010" -> 10
  • rounded cases:
    • s="-1_000_000_000_000" -> -2**31
    • s="1_000_000_000_000" -> 2**31-1

step2

class Solution:
    def myAtoi(self, s: str) -> int:
        SPACE = " "
        PLUS = "+"
        MINUS = "-"
        MIN_INT = -(2**31)
        MAX_INT = 2**31 - 1

        if not s:
            return 0
        num = 0

        # remove leading whitespaces
        index = 0
        while index < len(s) and s[index] == SPACE:
            index += 1
        # process sign
        sign = 1
        if index < len(s) and s[index] == PLUS:
            index += 1
        elif index < len(s) and s[index] == MINUS:
            sign = -1
            index += 1
        # conversion
        while index < len(s) and "0"<=s[index]<="9":
            digit = ord(s[index]) - ord("0")
            num = num * 10 + digit
            index += 1

        num = sign * num
        # rounding
        num = min(num, MAX_INT)
        num = max(num, MIN_INT)

        return num
  • 文字列のスライスを使わないようにした。これにより、O(n)の時間計算量とO(1)の空間計算量で実装できる
  • is_negativesignに変更した
    • sign1-1にしておくと、最後にnum=sign*numとするだけで済む

最後の変換部分のロジックを

digits.reverse()
for i, digit in enumerate(digits):
    num += digit * pow(10, i)

から

for digit in digits:
    num = num * 10 + digit

と書き換えた

  • digitsを逆順にする必要がなくなった

  • powを使わなくなった

  • num=0から始めて、num=num*10+digitを繰り返すことで、上の位の桁から順に処理できる

  • ord(s[pos]) - ord('0')の代わりにint(s[pos])とかも選択肢としてあり。とはいえこれをするんだったらint(s[index:])みたいにやっていいじゃんという気持ちにもなるので、この問題的にはint使わない方が空気が読めてそう。

  • ordを使う方法もある
    (ref: https://github.com/hayashi-ay/leetcode/pull/69/files)

    • Pythonでは文字列にも比較演算子が使えるので、"0" <= s[i] <= "9"のようにするのが良いかも
  • is_digitメソッドはASCIIコード以外にも対応したメソッドなので、今回は使わなくて良い

step3

class Solution:
    def myAtoi(self, s: str) -> int:
        if not s:
            return 0
        index = 0
        # Process whitespace
        SPACE = " "
        while index < len(s) and s[index] == SPACE:
            index += 1

        # Process signedness
        is_negative = False
        MINUS = "-"
        PLUS = "+"
        if index < len(s) and s[index] == MINUS:
            is_negative = True
            index += 1
        elif index < len(s) and s[index] == PLUS:
            index += 1

        # Process conversion
        num = 0
        # leading zeros
        while index < len(s) and s[index] == str(0):
            index += 1
        digits = [str(i) for i in range(10)]
        while index < len(s) and s[index] in digits:
            digit = int(s[index])
            num = num*10 + digit
            index += 1

        # apply signedness

        num = -num if is_negative else num

        # Process rounding
        INT_MIN = -2**31
        INT_MAX = 2**31 -1
        num = min(num, INT_MAX)
        num = max(num, INT_MIN)
        return num
  • ordや、文字列の比較を忘れていた
  • leading zero の処理を別途書いたが、これはなくても良かった

step4 (FB)

その他

次に解く問題の予告

if not s:
return 0
index = 0
# Process whitespace
Copy link

Choose a reason for hiding this comment

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

https://docs.python.org/ja/3.13/library/stdtypes.html#str.strip
こんなのがあるみたいです。こちらも使えるかもしれません。

# Process signedness
is_negative = False
MINUS = "-"
PLUS = "+"
Copy link

Choose a reason for hiding this comment

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

SPACE, MINUS, PLUSは見れば明らかなので変数に置かなくてもいいと思いました。


# apply signedness

num = -num if is_negative else num
Copy link

Choose a reason for hiding this comment

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

細かいですが、if is_negative: num *= -1ぐらいの書き方でもいいような気がします。

if not s:
return 0
# process sign
is_negative = False
Copy link

Choose a reason for hiding this comment

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

今回の問題では登場しませんが、否定的なニュアンスの bool 型の変数を否定する場合、二重否定となり、読み手に取って認知負荷がやや上がってしまいます。 bool 型の変数は肯定的なニュアンスの変数にしたほうが良いかもしれません。今回の場合は is_positive となると思います。

if not s:
return 0
# process sign
is_negative = False
Copy link

Choose a reason for hiding this comment

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

変数名が be 動詞で始まっていると、関数名のように見える場合があります。今回の場合は negative で十分だと思います。

INT_MIN = -2**31
INT_MAX = 2**31 -1
num = min(num, INT_MAX)
num = max(num, INT_MIN)

Choose a reason for hiding this comment

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

Python だと気にしなくても良いのですが、オーバーフローする状況での取り扱いも見てみると良いと思いました。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants