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
99 changes: 99 additions & 0 deletions hashmap/387.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
- 問題: [387. First Unique Character in a String](https://leetcode.com/problems/first-unique-character-in-a-string/)
- コメント集: [387. First Unique Character in a String](https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/mobilebasic#h.hdbcrwo3urck)
- [コメント1](https://discord.com/channels/1084280443945353267/1201211204547383386/1210484389692313610)
- LinkedHashMap を使って解く方針もある、Javaは残念ながら HashMap に保存した順序を保持してくれないが、最後に LinkedHahsMap を頭から走査して value に出現回数が1なら、そのインデックスの番号を返せば即答えになる
- Python は3.7からデフォルトで順序維持される
- [異常入力をどうハンドリングするか?](https://github.com/quinn-sasha/leetcode/pull/15#discussion_r1970861088)
- 異常入力が来た時にこのプログラムがどう言う挙動をするかというと、「返却する値がプログラムの目的と合致しない可能性がある。小文字でかつユニークでかつ最初に出現するもじのインデックスを返すわけではなくなる」
- じゃあ上記の時にどう言う挙動をしてくれた方がいいんだっけ?を考えると、例外を投げて呼び出し元にハンドリングしてもらうか、異常値として-1を返すか、気にせずそのまま処理する等がある
- 例としてあげた3パターンは当然このメソッドが呼ばれるコンテキストに依存するのでどんなケースがあるか大雑把に考えると、、
1. ユーザの入力等
2. バッチとかで夜動いている
- なんかがあって、1 はエラーメッセージが出た方が良さそうだから例外投げた方がいいんだろうし、後者は落ちていたほうがいいのだろう。。正解はない。
- 条件
- 1 <= s.length <= 10^5
- s consists of only lowercase English letters.
- 方針
- HashMap を使う方針
- 一度、s を走査して、文字と出現回数を登録していく
- 次のループで s を走査しつつ、mapを確認して出現回数が1のものがあればそのまま走査しているindexを返却する
- 時間計算量: `O(N)` ※HashMap 登録時の hash() は基本的に O(K)かかるが、今回は char が一文字であることから O(1)で済む
- 空間計算量: `O(1)` ※制約から、アルファベットは最大26文字なので

# 1st
- 初期方針
```java
class Solution {
public int firstUniqChar(String s) {
Map<char, int> frequency = new HashMap<>();
for (char character : s) {
frequency.put(character, frequency.getOrDefault(character, 0) + 1);
}

for (char character : s) {
if (frequency.get(character) == 1) {
return frequency.get(character);
}
}

return -1;
}
}
```
- 動くように修正
```java
class Solution {
public int firstUniqChar(String s) {
Map<Character, Integer> frequency = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
frequency.put(c, frequency.getOrDefault(c, 0) + 1);
}

for (int i = 0; i < s.length(); i++) {
if (frequency.get(s.charAt(i)) == 1) {
return i;
}
}

return -1;
}
}
```

# Claudeレビュー結果

## 387. First Unique Character in a String レビュー

### 文法・API
- `for (char c : s)` はJavaのStringに対して拡張for文は使えない。`charAt(i)` か `toCharArray()` を使う
- `characterIndex.getValue()` というメソッドは存在しない。`get()` を使う
- `s.get(index)` はStringのメソッドではない。`s.charAt(index)` を使う

### 計算量
- 空間計算量は `O(N)` としているが、`s` が小文字アルファベットのみという制約から `frequency` のエントリー数は最大26なので `O(1)`

### Integer比較
- `frequency.get(s.charAt(i)) == 1` の `==` はオブジェクトの参照比較になる。1はIntegerキャッシュ範囲内(-128〜127)なのでたまたま動くが、`.equals(1)` を使う方が意図が明確で安全
- 参考: [Java Language Specification - Boxing Conversion](https://docs.oracle.com/javase/specs/jls/se8/html/jls-5.html#jls-5.1.7)

# レビューを受けて書き直し
```java
class Solution {
public int firstUniqChar(String s) {
Map<Character, Integer> frequency = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
frequency.put(c, frequency.getOrDefault(c, 0) + 1);
}

for (int i = 0; i < s.length(); i++) {
if (frequency.get(s.charAt(i)).equals(1)) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

if (frequency.get(s.charAt(i)) == 1) {

と書いても、アンボクシングされるため、意図通り動くようです。

https://docs.oracle.com/javase/specs/jls/se17/html/jls-15.html#jls-15.21.1

If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6).

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

コメントありがとうございます!
等号演算子を使ったとき、片方がプリミティブであり、もう片方がアンボクシング可能ならアンボクシングされてから演算されるのですね。
Cluadeは Integer == Integer の比較になると言っていましたが、危うく嘘を信じ込むところでした。。
一次情報をもっと読み込むべきでした。

return i;
}
}

return -1;
}
}
```