Skip to content
Open
Show file tree
Hide file tree
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
107 changes: 107 additions & 0 deletions 387. First Unique Character in a String.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
愚直にやるなら前から1文字ずつ見ていってそれぞれ重複がないかを調べる。計算量は $O(n^2)$ で問題の設定上nは最大10^5になるので10^10になりTLEしそうなので別の解法を考える必要がある。
あらかじめユニークな文字を求めたあとで前から見ていけば $O(n)$ でいける。ユニークを求める方法は、各文字の登場回数をカウンティングしたりする感じで良さそう。
ただ上記の方法では2周する必要がある。後ろから見ていって、最後に初めて登場した文字を覚えていれば1回文字列を後ろからなめるだけで解けそう。← と思ったけどそんなことはなくて結構大変そう。


1st

文字の登場回数をあらかじめカウントする。
英アルファベット小文字をインデックス値に変換する処理は関数に切り出しても良いがtoo muchか。

```python
class Solution:
def firstUniqChar(self, s: str) -> int:
freq = [0] * 26
for c in s:
freq[ord(c) - ord('a')] += 1
for i, c in enumerate(s):
if freq[ord(c) - ord('a')] == 1:
return i
return -1
```

2nd

1stでは課題の制約で英アルファベット小文字のみだったので配列で管理したがdictを使う版でも解いた。Counterを使っても良い。

```python
class Solution:
def firstUniqChar(self, s: str) -> int:
freq = defaultdict(int)
for c in s:
freq[c] += 1
for i, c in enumerate(s):
if freq[c] == 1:
return i
return -1
```

Counterを使った版

```python
class Solution:
def firstUniqChar(self, s: str) -> int:
freq = Counter(s)
for i, c in enumerate(s):
if freq[c] == 1:
return i
return -1
```

3rd

1stと同じ。

```python
class Solution:
def firstUniqChar(self, s: str) -> int:
freq = [0] * 26
for c in s:
freq[ord(c) - ord('a')] += 1

Choose a reason for hiding this comment

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

listを使った解法にしたんですね。
想定外の文字列が入ってたらエラーになるので、dictのほうが安全かつ分かりやすいと思いました。
今回は英アルファベット小文字のみの制約があるので、気にし過ぎたコメントかもしれません。

Copy link
Owner Author

Choose a reason for hiding this comment

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

コメントありがとうございます。

想定外の文字列が入ってたらエラーになるので、dictのほうが安全かつ分かりやすいと思いました。

まあ何を想定するかですかねー。英アルファベット小文字が来ることが担保できる状況ならハッシュ処理が入らない固定長のlistを使った方が早いと思ったのでlistの解法にしました。アルファベットをインデックス値に変換する処理が入るのでその分複雑にはなりますね。

dictは前提条件が変わって文字種が増えた際などにそのまま使い回せるので良いと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

半年後に読んだ別の同僚が不安にならないか、環境の変化に対して頑健か、なのです。

まあでもとりあえず書くならdictの方が良い気がしてきました。そのうえで入力値の制約下でパフォーマンスを向上させたいなどの要件があった際にlistを使った方を書く方がいいですね。

for i, c in enumerate(s):
if freq[ord(c) - ord('a')] == 1:
return i
return -1
```

4th

1から3と違いsを1回だけなめる。登場したindexの位置と2回以上登場したかどうかは別に管理しても良いが一旦infが2回以上登場したと扱って1つで管理するようにした。
minを使っている部分は、Python3.7からdictへの追加順が仕様として保証されるようになったので他に書きようがある。
1から3だと`aaaaaaaaaa ... b`みたいな文字だと2周する必要があったのでこちらの方が1周で済む分良い。

```python
class Solution:
def firstUniqChar(self, s: str) -> int:
char_to_first_appear_index = {}
for index, c in enumerate(s):
if c in char_to_first_appear_index:
char_to_first_appear_index[c] = inf
else:
char_to_first_appear_index[c] = index
min_index = min(char_to_first_appear_index.values())
if min_index == inf:
return -1
return min_index
```

4-1をベースに書き直し。登場したindexの位置と重複しているかを別で管理するように。Pythonのdictの追加順が保存されていることを利用するように。
https://docs.python.org/3/library/stdtypes.html#dictionary-view-objects

```python
class Solution:
def firstUniqChar(self, s: str) -> int:
char_to_first_appear_index = {}
duplicated = set()
for index, c in enumerate(s):
if c in duplicated:
continue
if c in char_to_first_appear_index:
del char_to_first_appear_index[c]
duplicated.add(c)
continue
char_to_first_appear_index[c] = index
if not char_to_first_appear_index:
return -1
return next(iter(char_to_first_appear_index.values()))
```
20 changes: 20 additions & 0 deletions java.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
1st

```java
class Solution {
public int firstUniqChar(String s) {
HashMap<Character, Integer> charToFreq = new HashMap<>();

for (char c: s.toCharArray()) {
int prevFreq = charToFreq.getOrDefault(c, 0);
charToFreq.put(c, prevFreq + 1);
}
for (int i = 0; i < s.length(); i++) {
if (charToFreq.get(s.charAt(i)) == 1) {
return i;
}
}
return -1;
}
}
```