From 1a28b9cf611df06f50efa9ebf17e8f38ec3afda9 Mon Sep 17 00:00:00 2001 From: Ryohei Sato <130881456+Satorien@users.noreply.github.com> Date: Fri, 16 Jan 2026 10:35:46 +0900 Subject: [PATCH] Solved Arai60/8. String to Integer (atoi) --- Python3/8. String to Integer (atoi).md | 232 +++++++++++++++++++++++++ 1 file changed, 232 insertions(+) create mode 100644 Python3/8. String to Integer (atoi).md diff --git a/Python3/8. String to Integer (atoi).md b/Python3/8. String to Integer (atoi).md new file mode 100644 index 0000000..891a441 --- /dev/null +++ b/Python3/8. String to Integer (atoi).md @@ -0,0 +1,232 @@ +## Step 1. Initial Solution + +- 色々考えることが多いので順番に分けて処理する + - いくつか勘違いをしていて時間を取られた + - 1 << 31 - 1とすると 1 << 30になる + - if ‘ ‘: とするとTrue扱いになる + - 整数判定(str.isdigit()) + - - 1 // 10 は 0ではなく-1を返す +- もっときれいな書き方はあるだろうが取りあえずの実装 +- 特段難しいことはない処理でも多段階になるとどうしても作業効率が落ちてしまう + - どこかに全体設計を書いてから実装した方が良いのだろうか + +```python +POSITIVE = 1 +NEGATIVE = 0 +MAX_INT = (1 << 31) - 1 +MIN_INT = -(1 << 31) +NUMBERS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'} + +class Solution: + def myAtoi(self, s: str) -> int: + sign = POSITIVE + index = 0 + num = 0 + while index < len(s): + if s[index] == ' ': + index += 1 + continue + if s[index] == '-': + sign = NEGATIVE + index += 1 + break + if s[index] == '+': + index += 1 + break + if s[index] in NUMBERS: + break + return 0 + + while index < len(s): + if s[index] == '0': + index += 1 + continue + if s[index] in NUMBERS: + break + if s[index]: + return 0 + + while index < len(s): + if s[index] in NUMBERS: + if sign == POSITIVE and (MAX_INT - int(s[index])) // 10 - num < 0: + return MAX_INT + if sign == NEGATIVE and (MIN_INT + int(s[index])) // 10 + 1 + num > 0: + return MIN_INT + num = num * 10 + int(s[index]) + index += 1 + else: + break + return num if sign == POSITIVE else -num +``` + +### Complexity Analysis + +- 時間計算量:O(N) +- 空間計算量:O(1) + +## Step 2. Alternatives + +- まずは自分でリファクタリング + - 少しは見やすくなったがやはり複雑に見える + + ```python + MAX_INT = (1 << 31) - 1 + MIN_INT = -(1 << 31) + + class Solution: + def myAtoi(self, s: str) -> int: + index = 0 + while index < len(s): + if s[index] == ' ': + index += 1 + continue + break + + is_positive = True + if index == len(s): + return 0 + if s[index] == '+': + index += 1 + elif s[index] == '-': + index += 1 + is_positive = False + + num = 0 + while index < len(s) and s[index].isdigit(): + if is_positive and num > (MAX_INT - int(s[index])) // 10: + return MAX_INT + if not is_positive and -num < (MIN_INT + int(s[index])) // 10 + 1: + return MIN_INT + num = num * 10 + int(s[index]) + index += 1 + return num if is_positive else -num + ``` + +- https://github.com/tokuhirat/LeetCode/pull/59/files#diff-ed1cf3d46cfd4bddc1808bc5157b0cc7596de0a5858c3d1da82c0d1b1006874cR13-R29 + - 細かい処理をメソッドとして定義している + - 多少読みやすいが全体として長くは見える +- https://github.com/tokuhirat/LeetCode/pull/59/files#diff-ed1cf3d46cfd4bddc1808bc5157b0cc7596de0a5858c3d1da82c0d1b1006874cR79 + - 一桁のintはcase文くらいしか思いついていなかったが確かにordでも良い + - str.isdigit()についてもちゃんと理解していなかった + - `Digits include decimal characters and digits that need special handling, such as the compatibility superscript digits` +- https://github.com/tokuhirat/LeetCode/pull/59/files#diff-ed1cf3d46cfd4bddc1808bc5157b0cc7596de0a5858c3d1da82c0d1b1006874cR92 + - シフト演算子の優先順位が低いことはドキュメントにもあるらしい、盲点だった +- https://github.com/naoto-iwase/leetcode/pull/60/files#diff-663ed9c661571932db8d4d60eac1099f01e58022328775ad01b28bab535523e8R335 + - 最後の計算部分はvalueの方が良い名前だと感じた + - 先にsignを入れながら計算するのもこの形なら綺麗 +- https://github.com/naoto-iwase/leetcode/pull/60/files#diff-663ed9c661571932db8d4d60eac1099f01e58022328775ad01b28bab535523e8R278 + - このように正規表現を綺麗に書くのが一番簡潔 + - ただ、コメントにもある通り認知負荷は上がるので読む時間が短くなるかは微妙 + - 一か所を見て全体のルールを把握できるのは利点 + - `_PATTERN = re.compile(r"\s*([+-]?)(\d+)")` + - \s*: スペース0個以上 + - ([+-]?): +-のいずれか任意でグループ化 + - (\d+): 0-9が一つ以上 + - たまに書かないと忘れるので良い復習になった +- 少し間が空いてから再度書いたらまたかなり時間がかかった + - 関数化せずにそのまま書いてしまった方が読みやすい気はする + + ```python + MAX_INT = (1 << 31) - 1 + MIN_INT = -(1 << 31) + class Solution: + def myAtoi(self, s: str) -> int: + def skip_whitespace() -> None: + if s[self.index] == ' ': + self.index += 1 + return + self.checked_whitespace = True + + def get_sign() -> None: + if s[self.index] == '+': + self.sign = 1 + self.index += 1 + elif s[self.index] == '-': + self.sign = -1 + self.index += 1 + self.checked_sign = True + + def skip_zero() -> None: + if s[self.index] == '0': + self.index += 1 + elif s[self.index] in string.digits: + self.checked_zero = True + else: + self.index = len(s) + + def round_int() -> None: + if s[self.index] not in string.digits: + self.index = len(s) + return + if self.num > (MAX_INT - ch_to_i(s[self.index])) // 10: + self.num = MAX_INT + self.index = len(s) + return + elif self.num < (MIN_INT + ch_to_i(s[self.index])) // 10 + 1: + self.num = MIN_INT + self.index = len(s) + return + self.num = self.num * 10 + self.sign * ch_to_i(s[self.index]) + self.index += 1 + + def ch_to_i(ch: str) -> int: + return ord(ch) - ord('0') + + self.index = 0 + self.checked_whitespace = False + self.checked_sign = False + self.checked_zeros = False + self.sign = 1 + self.num = 0 + while self.index < len(s): + if not self.checked_whitespace: + skip_whitespace() + continue + if not self.checked_sign: + get_sign() + continue + if not self.checked_zero: + skip_zero() + continue + round_int() + return self.num + ``` + + +## Step 3. Final Solution + +- 分かりやすさを失わない範囲でなるべくコンパクトに書くように意識 + +```python +class Solution: + def myAtoi(self, s: str) -> int: + index = 0 + while index < len(s): + if s[index] != ' ': + break + index += 1 + if index >= len(s): + return 0 + + sign = 1 + if s[index] == '+': + index += 1 + elif s[index] == '-': + index += 1 + sign = -1 + + MAX_INT = (1 << 31) - 1 + MIN_INT = -(1 << 31) + num = 0 + while index < len(s): + if s[index] not in string.digits: + return num + next_digit = sign * (ord(s[index]) - ord('0')) + if num > (MAX_INT - next_digit) // 10: + return MAX_INT + if num < (MIN_INT - next_digit) // 10 + 1: + return MIN_INT + num = num * 10 + next_digit + index += 1 + return num +```