Skip to content
Open
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
212 changes: 212 additions & 0 deletions 8/8.md
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) {
Copy link

Choose a reason for hiding this comment

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

自分ならメンバー変数やメンバー関数のあいだに空行を空けると思います。

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."
ってある。
この二つって別に両立するんでしょうか?それとも日本語版のドキュメントが間違ってるんですかね(分かる方教えてください。。。)

Choose a reason for hiding this comment

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

今回のようにASCII範囲の数字であればどの文字コードでも共通のコードポイントの認識です。

Copy link

Choose a reason for hiding this comment

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

ソースコードの文字コードと実行環境の文字コードという概念があって、実行環境の文字コードは ASCII を包含していることは保証はされていません。たとえば、EBCDIC という文字コードがあります。

isdigit の仕様を追っていくと、たしかに、C99 では isdigit はロケールによらず 5.2.1 節に列挙されている10文字を指しているので、文字コードが何かはともかく、文字としてその10文字です。
ドラフト: http://www.open-std.org/JTC1/sc22/wg14/www/docs/n1256.pdf

Copy link
Owner Author

Choose a reason for hiding this comment

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

ありがとうございます。貼っていただいたドラフトのp205にも
The only functions in 7.4 whose behavior is not affected by the current locale are isdigit and isxdigit.
とあるので、この二つはロケールに影響されなさそうですね。


## 最終コード

```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;
}
};

Choose a reason for hiding this comment

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

良いですが、long long を使わずオーバーフローをしないようにチェックしつつ計算するパターンも書いてみると勉強になると思います。

```