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
158 changes: 158 additions & 0 deletions 8StringToInteger(atoi).md
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
問題: https://leetcode.com/problems/string-to-integer-atoi/description/

### Step 1
- なんか帳尻合わせの汚いコードが出来上がった印象
Copy link

Choose a reason for hiding this comment

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

ステートマシンはだいたい大変になります。

- テストケース
- s="" -> 0
- s="1" -> 1
- s="11" -> 11
- s="1 01" -> 101
- s="-1" -> -1
- s="hello" -> 0
- s="1hello56" -> 1
- s="999999999999" -> math.MaxInt32

```Go
func myAtoi(s string) int {
isNegative := false
isNumber := false
Copy link

Choose a reason for hiding this comment

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

ステートマシンを作る場合、ステートを表す enum 相当のものを使う方法もあります。

以下 C++ の記述です。

class Solution {
private:
    enum State {
        Whitespace,
        Signedness,
        Conversion,
    };
public:
    int myAtoi(string s) {
        State state = Whitespace;
        int64_t absolute = 0;
        int sign = 1;
        int index = 0;
        while (index < s.size()) {
            char ch = s[index];
            switch (state) {
                case Whitespace:
                    if (ch == ' ') {
                        ++index;
                        continue;
                    } else {
                        state = Signedness;
                    }
                    break;

                case Signedness:
                    if (ch == '+') {
                        sign = 1;
                        ++index;
                        state = Conversion;
                    } else if (ch == '-') {
                        sign = -1;
                        ++index;
                        state = Conversion;
                    } else {
                        state = Conversion;
                    }
                    break;

                case Conversion:
                    if (!isdigit(ch)) {
                        // HACK: Exit loop by setting index to s.size();
                        index = s.size();
                        continue;
                    }
                    absolute *= 10;
                    absolute += ch - '0';
                    ++index;
                    if (absolute > std::numeric_limits<int>::max()) {
                        // HACK: Exit loop by setting index to s.size();
                        index = s.size();
                        continue;
                    }
                    break;
            }
        }

        absolute *= sign;
        absolute = std::clamp<int64_t>(absolute, std::numeric_limits<int>::min(), std::numeric_limits<int>::max());
        return absolute;
    }
};

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほどです。現在の状態がわかりやすいですね。

endParse := false
var result int64
for _, c := range s {
switch {
case unicode.IsLetter(c):
endParse = true
case !isNumber && c == ' ':
continue
case !isNumber && c == '-':
isNegative = true
isNumber = true
case !isNumber && c == '+':
isNegative = false
isNumber = true
case unicode.IsDigit(c):
isNumber = true
d := int64(c - '0')
result = result*10 + d
if isNegative && result > math.MaxInt32 {
return math.MinInt32
}
if !isNegative && result >= math.MaxInt32 {
return math.MaxInt32
}
default:
endParse = true
}
if endParse {
break
}
}
if isNegative {
return -int(result)
}
return int(result)
}
```

### Step 2
#### 2a
- https://github.com/olsen-blue/Arai60/pull/60/files#diff-f2b395d63173ac2f2d3c547e8f9e8e07c9acfbeb6b5da935bb28d83d8bcc7a04R141
- step1 のようにまとめてやってしまうのではなく、誘導に従って一つずつ処理したほうがよっぽど見やすい
- ワーキングメモリの話
- https://github.com/katsukii/leetcode/pull/9/files#diff-4ac2258a9828437ca7cab8fbd9970eecd069418f46ad2e8464424478e58a4ae4R202
- オーバーフローの確認の仕方
- なるほど、足す前に確認する方が、足した後に符号などで判定するより楽
- だいぶきれいに書けたので満足

```Go
func myAtoi(s string) int {
index := 0
for index < len(s) && s[index] == ' ' {
index++
}
if index == len(s) {
return 0
}
sign := 1
if s[index] == '+' {
index++
} else if s[index] == '-' {
sign = -1
index++
}
result := 0
for ; index < len(s); index++ {
if !('0' <= s[index] && s[index] <= '9') {
break
}
d := int(s[index] - '0')
switch sign {
case 1:
if result > (math.MaxInt32-d)/10 {
return math.MaxInt32
}
result = result*10 + d
case -1:
if result < (math.MinInt32+d)/10 {
return math.MinInt32
}
result = result*10 - d
}
}
return result
}
```

- マルチバイト文字が含まれている場合にどうなるか不安になった
- 上記コードだとマルチバイト文字の先頭バイトが ' ', '-', '+' と一緒だとおかしなことになる
- 調べたところ、マルチバイト文字は ascii と共存できるよう、先頭バイトは ascii の範囲外になるよう設計されているので、
Copy link

Choose a reason for hiding this comment

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

これは、文字コードによって違うのですが、JISコードは先頭バイトが ASCII とぶつかります。
最近は、あまり気にしなくていいのではないでしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

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

最近あまり気にしなくて良くなったのはなぜですか?

Copy link

Choose a reason for hiding this comment

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

JIS コードが使われることはあまりなく、十分にユニコードが使われるようになったからですね。

そういう問題は起きない

### Step 3

```Go
func myAtoi(s string) int {
index := 0
for index < len(s) && s[index] == ' ' {
index++
}
if index == len(s) {
return 0
}

sign := 1
if s[index] == '+' {
index++
} else if s[index] == '-' {
sign = -1
index++
}

result := 0
for ; index < len(s); index++ {
if !('0' <= s[index] && s[index] <= '9') {
break
}
d := int(s[index] - '0')
switch sign {
case 1:
if result > (math.MaxInt32-d)/10 {
return math.MaxInt32
}
result = result*10 + d
case -1:
if result < (math.MinInt32+d)/10 {
return math.MinInt32
}
result = result*10 - d
}
}
return result
}
```

### CS
- atoi
- i は int なのでわかるが、なぜ a なのか疑問だったので調べた
- https://stackoverflow.com/questions/2909768/where-did-the-name-atoi-come-from
- a は ASCII から来ているらしい