-
Notifications
You must be signed in to change notification settings - Fork 0
8. String to Integer (atoi) #59
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,212 @@ | ||
| ## 何も見ずに解いてみる | ||
|
|
||
| 初号機 | ||
|
|
||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| int myAtoi(string s) { | ||
| const unordered_map<char, int> DIGIT_MAPPING = { | ||
| {'0', 0}, | ||
| {'1', 1}, | ||
| {'2', 2}, | ||
| {'3', 3}, | ||
| {'4', 4}, | ||
| {'5', 5}, | ||
| {'6', 6}, | ||
| {'7', 7}, | ||
| {'8', 8}, | ||
| {'9', 9}, | ||
| }; | ||
| int sign = 1; | ||
| auto it = s.begin(); | ||
| while (it != s.end() && *it == ' ') { | ||
| ++it; | ||
| } | ||
| if (it != s.end() && (*it == '+' || *it == '-')) { | ||
| if (*it == '+') { | ||
| sign = 1; | ||
| } | ||
| if (*it == '-') { | ||
| sign = -1; | ||
| } | ||
| ++it; | ||
| } | ||
| while (it != s.end() && *it == '0') { | ||
| ++it; | ||
| } | ||
| auto rend = reverse_iterator(it); | ||
| while (it != s.end() && DIGIT_MAPPING.contains(*it)) { | ||
| ++it; | ||
| } | ||
| auto rit = reverse_iterator(it); | ||
| long long num = 0; | ||
| long long power = 1; | ||
| while (rit != rend) { | ||
| if (power > numeric_limits<int>::max()) { | ||
| if (sign == 1) { | ||
| return numeric_limits<int>::max(); | ||
| } else { | ||
| return numeric_limits<int>::min(); | ||
| } | ||
| } | ||
| num += DIGIT_MAPPING.at(*rit) * power; | ||
| power *= 10; | ||
| ++rit; | ||
| } | ||
| if (num * sign > numeric_limits<int>::max()) { | ||
| return numeric_limits<int>::max(); | ||
| } | ||
| if (num * sign < numeric_limits<int>::min()) { | ||
| return numeric_limits<int>::min(); | ||
| } | ||
| return num * sign; | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| 二号機(あんまり見やすくならなかった。。。) | ||
|
|
||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| int myAtoi(string s) { | ||
| string_view whitespace_removed = removeLeadingWhitespace(s); | ||
| auto [sign, sign_removed] = checkAndRemoveSign(whitespace_removed); | ||
| string_view zeroes_removed = removeLeadingZeroes(sign_removed); | ||
| string_view valid_digits = extractValidDigits(zeroes_removed); | ||
| if (valid_digits.size() > 10) { | ||
| if (sign == 1) { | ||
| return numeric_limits<int>::max(); | ||
| } else { | ||
| return numeric_limits<int>::min(); | ||
| } | ||
| } | ||
| long long num = 0; | ||
| long long power = 1; | ||
| for (auto rit = valid_digits.rbegin(); rit != valid_digits.rend(); ++rit) { | ||
| num += DIGIT_MAPPING.at(*rit) * power; | ||
| power *= 10; | ||
| } | ||
| return clamp<long long>(num * sign, numeric_limits<int>::min(), numeric_limits<int>::max()); | ||
| } | ||
| private: | ||
| const unordered_map<char, int> DIGIT_MAPPING = { | ||
| {'0', 0}, | ||
| {'1', 1}, | ||
| {'2', 2}, | ||
| {'3', 3}, | ||
| {'4', 4}, | ||
| {'5', 5}, | ||
| {'6', 6}, | ||
| {'7', 7}, | ||
| {'8', 8}, | ||
| {'9', 9}, | ||
| }; | ||
| string_view removeLeadingWhitespace(string_view s) { | ||
| auto it = s.begin(); | ||
| while (it != s.end() && *it == ' ') { | ||
| ++it; | ||
| } | ||
| return string_view(it, s.end()); | ||
| } | ||
| tuple<int, string_view> checkAndRemoveSign(string_view s) { | ||
| if (s.empty()) { | ||
| return {1, s}; | ||
| } | ||
| if (s.front() == '+') { | ||
| return {1, string_view(s.begin() + 1, s.end())}; | ||
| } | ||
| if (s.front() == '-') { | ||
| return {-1, string_view(s.begin() + 1, s.end())}; | ||
| } | ||
| return {1, s}; | ||
| } | ||
| string_view removeLeadingZeroes(string_view s) { | ||
| auto it = s.begin(); | ||
| while (it != s.end() && *it == '0') { | ||
| ++it; | ||
| } | ||
| return string_view(it, s.end()); | ||
| } | ||
| string_view extractValidDigits(string_view s) { | ||
| auto it = s.begin(); | ||
| while (it != s.end() && DIGIT_MAPPING.contains(*it)) { | ||
| ++it; | ||
| } | ||
| return string_view(s.begin(), it); | ||
| } | ||
| }; | ||
| ``` | ||
|
|
||
| ## 他の人のコードを見てみる | ||
|
|
||
| https://github.com/tokuhirat/LeetCode/pull/59/files | ||
| https://github.com/ryosuketc/leetcode_arai60/pull/59/files | ||
| https://github.com/Ryotaro25/leetcode_first60/pull/64/files | ||
| https://github.com/philip82148/leetcode-swejp/pull/6/files | ||
|
|
||
| 有効桁のところは別に後ろから見なくても、結果それ自体を桁が増えるごとに10倍していけば数値にできるのか。 | ||
|
|
||
| '0'から'9'までの数字がcharをintに変換したときにこの順に連続で並んでることは保証されているっぽい? | ||
| https://stackoverflow.com/questions/9416926/are-the-character-digits-0-9-required-to-have-contiguous-numeric-values | ||
| https://en.cppreference.com/w/c/language/charset.html | ||
|
|
||
| https://cpprefjp.github.io/reference/string/basic_string.html | ||
| "find_first_not_of"とかは使えそうかも。 | ||
|
|
||
| https://en.cppreference.com/w/cpp/string/byte/atoi.html | ||
| サンプル実装が書いてある(オーバーフローのチェックはしてないっぽい) | ||
| C言語の文字列はchar[]として保存されてて最後に末端文字なるものがついているらしく、イテレーターが終端にたどり着いたかどうかを気にしなくていいらしい。 | ||
| (*strが終端文字になった時点で全部スルーされる) | ||
| [c_str](https://cpprefjp.github.io/reference/string/basic_string/c_str.html)というので変換すればこの問題でも終端チェックしなくてもよくなるかも。 | ||
|
|
||
| iswhitespaceの略かなと思ってiswspaceのページも開いたらワイド文字とかいう変な概念出てきた | ||
| https://en.cppreference.com/w/cpp/string/wide/iswspace.html | ||
| https://ja.wikipedia.org/wiki/%E3%83%AF%E3%82%A4%E3%83%89%E6%96%87%E5%AD%97 | ||
|
|
||
| https://github.com/philip82148/leetcode-swejp/pull/6/files#r1853735872 | ||
| オーバーフローするかどうかを判定してくれる組み込み関数もあるらしい | ||
|
|
||
| std::isdigitについて、 | ||
| [日本語版のドキュメント](https://cpprefjp.github.io/reference/cctype/isdigit.html)には | ||
| 「ch が数字かどうかを判定する(判定はロケールの影響を受ける)。」 | ||
| ってあるけど、 | ||
| [英語版のドキュメント](https://en.cppreference.com/w/cpp/string/byte/isdigit.html)には | ||
| "isdigit and isxdigit are the only standard narrow character classification functions that are not affected by the currently installed C locale." | ||
| ってある。 | ||
| この二つって別に両立するんでしょうか?それとも日本語版のドキュメントが間違ってるんですかね(分かる方教えてください。。。) | ||
|
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. 今回のようにASCII範囲の数字であればどの文字コードでも共通のコードポイントの認識です。 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. ソースコードの文字コードと実行環境の文字コードという概念があって、実行環境の文字コードは ASCII を包含していることは保証はされていません。たとえば、EBCDIC という文字コードがあります。 isdigit の仕様を追っていくと、たしかに、C99 では isdigit はロケールによらず 5.2.1 節に列挙されている10文字を指しているので、文字コードが何かはともかく、文字としてその10文字です。
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. ありがとうございます。貼っていただいたドラフトのp205にも |
||
|
|
||
| ## 最終コード | ||
|
|
||
| ```cpp | ||
| class Solution { | ||
| public: | ||
| int myAtoi(string s) { | ||
| auto it = s.begin(); | ||
| while (it != s.end() && isspace(*it)) { | ||
| ++it; | ||
| } | ||
| int sign = 1; | ||
| if (it != s.end() && (*it == '+' || *it == '-')) { | ||
| if (*it == '-') { | ||
| sign = -1; | ||
| } | ||
| ++it; | ||
| } | ||
| long long num = 0; | ||
| while (it != s.end() && isdigit(*it)) { | ||
| int digit = *it - '0'; | ||
| num = num * 10 + digit * sign; | ||
| if (num > INT_MAX) { | ||
| return INT_MAX; | ||
| } | ||
| if (num < INT_MIN) { | ||
| return INT_MIN; | ||
| } | ||
| ++it; | ||
| } | ||
| return num; | ||
| } | ||
| }; | ||
|
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. 良いですが、long long を使わずオーバーフローをしないようにチェックしつつ計算するパターンも書いてみると勉強になると思います。 |
||
| ``` | ||
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.
自分ならメンバー変数やメンバー関数のあいだに空行を空けると思います。