Skip to content
Merged
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
25 changes: 25 additions & 0 deletions 387. First Unique Character in a String/note.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# step1
自分で解いた。

# step1-1
すぐに解法が浮かんだ。
1回目の走査で文字をカウントして、2回目の走査でカウント1のもののindexを返すという考え。
時間計算量:O(n) n:文字列の長さ、空間計算量:O(n)

# step1-2
次に解法が浮かんだ。小文字英字しか使わないパターンでは配列を使ったカウントがよく使われるイメージだったのですぐ実装できた。
ただ実際の入力は英小文字以外も入ってるくる可能性があるので、条件を交渉するか1改善案くらいのイメージ。
時間計算量:O(n) n:文字列の長さ、空間計算量:O(1)
実行時間がかなりはやくなったのはboxing/unboxingのオーバーヘッドのせいかなと予想。

# step2
他の人、もしくは回答をみて参考にしたもの

# step2-1
https://github.com/seal-azarashi/leetcode/pull/15/files
を参考にした。特に入力条件としてサロゲートなどを意識できていなかったので大変勉強になった。(odaさんのコメントのこちらも:https://note.com/ttuusskk/n/n1bff5d8e638c)
マジックナンバーを使わないようにいくつか定数を設定した。

# step3
いただいたコメントを元に修正したもの
特に変数名について意味をもう少し具体的にして、読む人が大体正しくイメージができるくらいにすることを忘れないようにしたい。
17 changes: 17 additions & 0 deletions 387. First Unique Character in a String/step1-1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Solution {
// leetcode
public int firstUniqChar(String s) {
Map<Character, Integer> indices = new HashMap<>();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

変数名 indices から、文字と出現頻度のマップだということが分かりにくく感じました。 charcterToCountscharacterToFrequencies といった、文字と出現頻度のマップであることが想起しやすい変数名を付けることをお勧めいたします。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

step1-2 以降のように、配列を使ったほうが処理が軽くなると思います。

for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
indices.put(c, indices.getOrDefault(c, 0) + 1);
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (indices.get(c) == 1) {
return i;
}
}
return -1;
}
}
17 changes: 17 additions & 0 deletions 387. First Unique Character in a String/step1-2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Solution {
// leetcode
public int firstUniqChar(String s) {
int[] cs = new int[26];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

cs という変数名からは、中にどのような値が入っているかを推測するのが難しく感じました。 step 2-1 のように推測しやすい英単語、または英語句を使用するとよいと思います。また、配列やコレクションのように、複数の値が含まれる変数については、最後の単語を複数形にすると、理解しやすくなると思います。

for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
cs[c - 'a']++;
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (cs[c - 'a'] == 1) {
return i;
}
}
return -1;
}
}
21 changes: 21 additions & 0 deletions 387. First Unique Character in a String/step2-1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
// leetcode
private static final int ALPHABET_SIZE = 26;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

自分なら NUM_ALPHABETS と名付けると思います。ここは好みかもしれません。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

小文字のアルファベットの数のことなので、大きさを表す size は適当でないように思います。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

このままでも良さそうです。https://csrc.nist.gov/glossary/term/alphabet_size

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

なんと、一般的な語彙だったのですね。失礼しました。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ちょっと上記のソース読んでみたのですが、どうやら上記の alphabet とは一般的に想起される abc... とは違うみたいです。全く専門外なのであまり自信がないですが、ソースになっている論文での使われ方を見るに size は可変であるようなので、恐らく形式言語における alphabet の事を指しているのかと思われます。

abc... の方の alphabet について、これの size が a-z の数26を指すという例は、私の方で少しググった限りでは見つけられませんでした。
ただ wikipedia に "An alphabet may have any cardinality ("size") and, depending on its purpose, may be finite (e.g., the alphabet of letters "a" through "z")" とあるように、形式言語における alphabet は abc... の方の alphabet も指し得るみたいなので、一般的かはさておき間違いではないですね。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

(もし英語ネイティブで形式言語についての知識がある方がいらっしゃったら意見伺いたい...)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

https://www.ldoceonline.com/dictionary/alphabet

a set of letters, arranged in a particular order, and used in writing

setに対してsizeを使うのは、普通の表現だと思います。
例:https://zoo.cs.yale.edu/classes/cs467/2005f/course/handouts/ho15.pdf
image

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

setに対してsizeを使うのは、普通の表現だと思います。

なるほど確かに。

private static final char ALPHABET_OFFSET = 'a';
public static final int NOT_FOUND = -1;

public int firstUniqChar(String s) {
int[] frequency = new int[ALPHABET_SIZE];
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

頻度の配列であることがわかるようにfrequenciesと複数形にすべきではないかと、自分は以前にnodaさんからコメントをもらいました

rihib/leetcode#34 (comment)

for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
frequency[c - ALPHABET_OFFSET]++;
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (frequency[c - ALPHABET_OFFSET] == 1) {
return i;
}
}
return NOT_FOUND;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

LeetCodeの制約上仕方がないですが、そもそも-1を返すことが良いことではないので、これが本当に良いのか(根本的な解決になっていないので)については個人的には疑問があります。それよりもLeetCodeの制約がないのであれば、どのように書くかということについて論じる(または実際に書いてみる)ほうが良いのではないかと思います。

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.

おっしゃるとおり実際のシステムだったら
・-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.

あ、はい、ここは「使う人の気持ちになって私はこうするのが一番いいと思ったのでこうしたのだ、もしかしたらもっといい方法があるのかもしれないが、少なくとも私はそう判断したのだ」と言えるならばいいと思います。

小学校の低学年の頃に、母に「きれいな字を書けるかは能力であるから人による。しかし、誠実な字を書くことは誰にでもできる。誠実な字とは読み手に伝えようという意思を持った字である。」といわれたことを思い出します。

}
}
21 changes: 21 additions & 0 deletions 387. First Unique Character in a String/step3-1.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
class Solution {
// leetcode
private static final int NUM_ALPHABETS = 26;
private static final char ALPHABET_OFFSET = 'a';
public static final int NOT_FOUND = -1;

public int firstUniqChar(String s) {
int[] frequencies = new int[NUM_ALPHABETS];
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
frequencies[c - ALPHABET_OFFSET]++;
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (frequencies[c - ALPHABET_OFFSET] == 1) {
return i;
}
}
return NOT_FOUND;
}
}
17 changes: 17 additions & 0 deletions 387. First Unique Character in a String/step3-2.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class Solution {
// leetcode
public int firstUniqChar(String s) {
Map<Character, Integer> characterToFrequencies = new HashMap<>();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
characterToFrequencies.put(c, characterToFrequencies.getOrDefault(c, 0) + 1);
}
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (characterToFrequencies.get(c) == 1) {
return i;
}
}
return -1;
}
}