From 1a45bb32134d2ed017fd2dcc68fd1c9a73b2b661 Mon Sep 17 00:00:00 2001 From: Tomoki Sadamori Date: Sat, 18 Jul 2026 14:47:22 +0900 Subject: [PATCH] =?UTF-8?q?step3=E3=81=BE=E3=81=A7=E5=AE=8C=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 0387-first-unique-char-in-str/memo.md | 69 ++++++++++++++++++++++++++ 0387-first-unique-char-in-str/step1.py | 26 ++++++++++ 0387-first-unique-char-in-str/step2.py | 30 +++++++++++ 3 files changed, 125 insertions(+) create mode 100644 0387-first-unique-char-in-str/memo.md create mode 100644 0387-first-unique-char-in-str/step1.py create mode 100644 0387-first-unique-char-in-str/step2.py diff --git a/0387-first-unique-char-in-str/memo.md b/0387-first-unique-char-in-str/memo.md new file mode 100644 index 0000000..48dbab3 --- /dev/null +++ b/0387-first-unique-char-in-str/memo.md @@ -0,0 +1,69 @@ +# 387. First Unique Character in a String + +## Step 1 +- 一回文字列を頭から舐めて文字:出現数で辞書に登録 +- もう一度文字列を走査して、出現数が1の文字のインデックス(かなければ-1)を返す +- 時間、空間ともにO(n) +- 入力が<= 10^5であることを考えるとPythonでも0.1秒収まるくらい?(平均的な処理能力を10^8/sec、Pythonがそこから最悪100倍で見積もっています、初めてなので勘違いしている点やリファレンスがあればコメントください) + +```python +class Solution: + def firstUniqChar(self, s: str) -> int: + appearences: dict[str, int] = {} + + for c in s: + if c in appearences: + appearences[c] += 1 + else: + appearences[c] = 1 + + for c in s: + if appearences[c] == 1: + return s.index(c) + + return -1 +``` + +## Step 2 +- enumerate()を使っている人が多かった。str.index()は文字列を走査し直す(当然でした)のでここで最悪O(n^2)になっている。 +- collections.Counter()という便利なやつがいる。 +```python +from collections import Counter + + +class Solution: + def firstUniqChar(self, s: str) -> int: + appearences = Counter(s) + + for i, c in enumerate(s): + if appearences[c] == 1: + return i + + return -1 +``` + +- OrderDictという要素の順番を保持してくれるものがある。辞書の値に出現回数ではなく最初に見つけたインデックスを保存し、二回目以降の出現があればそれを潰しておく。二回目のループのとき、辞書は文字列に登場した順番通りになっているので、潰れていないインデックスが出た瞬間それを返して問題ない。 +- Python3.7以降ふつうのdictも追加順を保持しているらしく(Dictionaries preserve insertion order.(https://docs.python.org/3/library/stdtypes.html#mapping-types-dict))、そのままdictで実装した。OrderDictをインポートしたほうがいいんでしょうか? +- 2回目のループが最長26回で済むのでだいぶ効率的。 + +```python +class Solution2: + def firstUniqChar(self, s: str) -> int: + seen_index:dict[str, int] = {} + duplicated = -1 + + for i, c in enumerate(s): + if c in seen_index: + seen_index[c] = duplicated + else: + seen_index[c] = i + + for idx in seen_index.values(): + if idx != duplicated: + return idx + + return -1 +``` + +## Step 3 +- OrderDictのほうが効率的かつ明示的だと思ったのでそちらで三回実装。 \ No newline at end of file diff --git a/0387-first-unique-char-in-str/step1.py b/0387-first-unique-char-in-str/step1.py new file mode 100644 index 0000000..46eb82e --- /dev/null +++ b/0387-first-unique-char-in-str/step1.py @@ -0,0 +1,26 @@ +class Solution: + def firstUniqChar(self, s: str) -> int: + appearences: dict[str, int] = {} + + for c in s: + if c in appearences: + appearences[c] += 1 + else: + appearences[c] = 1 + + for c in s: + if appearences[c] == 1: + return s.index(c) + + return -1 + + +def main() -> None: + Solver = Solution() + s = "loveleetcode" + res = Solver.firstUniqChar(s) + print(res) + + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/0387-first-unique-char-in-str/step2.py b/0387-first-unique-char-in-str/step2.py new file mode 100644 index 0000000..e79b0c3 --- /dev/null +++ b/0387-first-unique-char-in-str/step2.py @@ -0,0 +1,30 @@ +from collections import Counter + + +class Solution: + def firstUniqChar(self, s: str) -> int: + appearences = Counter(s) + + for i, c in enumerate(s): + if appearences[c] == 1: + return i + + return -1 + + +class Solution2: + def firstUniqChar(self, s: str) -> int: + seen_index:dict[str, int] = {} + duplicated = -1 + + for i, c in enumerate(s): + if c in seen_index: + seen_index[c] = duplicated + else: + seen_index[c] = i + + for idx in seen_index.values(): + if idx != duplicated: + return idx + + return -1