diff --git a/problems/387.first-unique-character-in-a-string/memo.md b/problems/387.first-unique-character-in-a-string/memo.md new file mode 100644 index 0000000..216fff1 --- /dev/null +++ b/problems/387.first-unique-character-in-a-string/memo.md @@ -0,0 +1,48 @@ +## step1 +- charactorがsの中に入っているかを愚直に見ていくのは? + - 10^5の場合setにしても大丈夫な大きさなはず + - 文字列が含んでいるかのinの判定は早いと聞いたことがあるが、内部の実装まで知らないのでstep2で調べたい + - 計算量はO(N^2) +- Counterの方が確実かもしれない + - Counterの場合はO(N) + - 見つからなかった場合の-1は定数(NO_UNIQUE_VALUE_RESPONSEのような感じ?)に入れておくべきか迷った + - そもそも-1を返すのはやめたい気持ちがある(Noneの方が後々indexとして使おうとするとエラーになるのでいいのではないかと思っている) + +## step2 +- chaが組み込み関数なのでcとしたが、charの方が良かったかもしれない +- 他の人のコードをみる +- https://github.com/Hiroto-Iizuka/coding_practice/pull/15/files + - `この問題は LRU に類似したデータ構造を私は連想しましたが、しなくてもいいかの境界くらいです。よく聞かれるので知っていてもいいかもしれません。(よく聞かれる理由は知らなくても作れていいだろうと思われるからですね。)` +- https://github.com/mamo3gr/arai60/pull/15/files + - `next`や`iter`に馴染みが無かったので調べる + - https://docs.python.org/ja/3/library/functions.html#next + - https://docs.python.org/ja/3/library/functions.html#iter + - こういうのを使っていると上級者なんだろうなと思う + - 自分から書く選択肢に全くないのでなんとかしたい気持ち +- https://github.com/yas-2023/leetcode_arai60/pull/15/files + - ベンチマークすごい + - 自分もloopの際に省略せずindex, characterと書いた方が良いかも + - inを使うよりもfindとrfindの方がネイティブで実行されるため良いのか? + - https://docs.python.org/ja/3/library/stdtypes.html#str.find + - `注釈 find() メソッドは、 sub の位置を知りたいときにのみ使うべきです。 sub が部分文字列であるかどうかのみを調べるには、 in 演算子を使ってください:` + - inは内部では`文字列やバイト列型については、 x in y は x が y の部分文字列であるとき、かつそのときに限り True になります。これは y.find(x) != -1 と等価です。空文字列は、他の任意の文字列の部分文字列とみなされます。従って "" in "abc" は True を返すことになります。`なので速度的には変わらないみたい + - `この方法ですと、入力が"aaaaaa....aab"みたいな時にforループが何度もaをチェックしてしまうため、個人的にはStep 1のように文字ごとにfirst indexを覚えておく方が好ましいと思います。計算量は同じO(n)ですが、場合によっては2倍ほどになる気がします。` + - counter作るのでO(N)でloopで0(N)なので実質2回forのloopをまわしているが、2回目は種類が少ない文字列側で見ていくと確かに良さそうだと思った +- https://github.com/Yuto729/LeetCode_arai60/pull/20/files + - 上記のコメントで出てきたLRUライクの意味が実装を見てわかった + - 2回目に出てきた時にdictのkeyを消して、あとはseenにあるかを見て最後に一番indexが早いものからdictに追加されたはずなので.values()で取り出す流れ + - Counterも追加した順番が保持されるのかは気になったので後で調べる + - `Counter は ハッシュ可能 なオブジェクトをカウントする dict のサブクラスです。これは、要素を辞書のキーとして保存し、そのカウントを辞書の値として保存するコレクションです。カウントは、0 や負のカウントを含む整数値をとれます。 Counter クラスは、他の言語のバッグや多重集合のようなものです。` + - https://docs.python.org/ja/3/library/collections.html#collections.Counter + +- step2に1回だけ走査する方法(1passというらしい) + - O(N) +- step2-2にCounterで辞書の入力順を利用した文字列側でのloopで解く方法を書いてみる + - カウンター作成でO(N), 文字のloopでO(M), findでO(N) Mは文字の種類 + +## step3 +- 1passの方法とカウンターの方法で2回ずつ書いてみる +- カウンターの方法の方が見た時に何しているか理解するのに時間がかからない気がする +- step2の時に他の人のコード見て思ったが、誰も見つからなかった時の-1を定数に入れたりしてなかったのでしないのが普通の感覚なのかと思った + - 自分も普段は絶対しないと思うが、いきなり−1が出てきて減点されないかどうかが少し心配だった + diff --git a/problems/387.first-unique-character-in-a-string/step1-2.py b/problems/387.first-unique-character-in-a-string/step1-2.py new file mode 100644 index 0000000..6f98fac --- /dev/null +++ b/problems/387.first-unique-character-in-a-string/step1-2.py @@ -0,0 +1,21 @@ +# +# @lc app=leetcode id=387 lang=python3 +# +# [387] First Unique Character in a String +# + +# @lc code=start +import collections + + +class Solution: + def firstUniqChar(self, s: str) -> int: + charactor_counter = collections.Counter(s) + for i, c in enumerate(s): + if charactor_counter[c] == 1: + return i + + return -1 + + +# @lc code=end diff --git a/problems/387.first-unique-character-in-a-string/step1.py b/problems/387.first-unique-character-in-a-string/step1.py new file mode 100644 index 0000000..19f6678 --- /dev/null +++ b/problems/387.first-unique-character-in-a-string/step1.py @@ -0,0 +1,17 @@ +# +# @lc app=leetcode id=387 lang=python3 +# +# [387] First Unique Character in a String +# + +# @lc code=start +class Solution: + def firstUniqChar(self, s: str) -> int: + for i, c in enumerate(s): + if c not in s[:i] and c not in s[i + 1 :]: + return i + + return -1 + + +# @lc code=end diff --git a/problems/387.first-unique-character-in-a-string/step2-2.py b/problems/387.first-unique-character-in-a-string/step2-2.py new file mode 100644 index 0000000..920e6f4 --- /dev/null +++ b/problems/387.first-unique-character-in-a-string/step2-2.py @@ -0,0 +1,22 @@ +# +# @lc app=leetcode id=387 lang=python3 +# +# [387] First Unique Character in a String +# + +# @lc code=start +import collections + + +class Solution: + def firstUniqChar(self, s: str) -> int: + charactor_counter = collections.Counter(s) + + for charactor, count in charactor_counter.items(): + if count == 1: + return s.find(charactor) + + return -1 + + +# @lc code=end diff --git a/problems/387.first-unique-character-in-a-string/step2.py b/problems/387.first-unique-character-in-a-string/step2.py new file mode 100644 index 0000000..271492a --- /dev/null +++ b/problems/387.first-unique-character-in-a-string/step2.py @@ -0,0 +1,32 @@ +# +# @lc app=leetcode id=387 lang=python3 +# +# [387] First Unique Character in a String +# + +# @lc code=start + + +class Solution: + def firstUniqChar(self, s: str) -> int: + charactor_to_index = dict() + seen_charactors = set() + + for index, charactor in enumerate(s): + if charactor in charactor_to_index: + del charactor_to_index[charactor] + continue + + if charactor in seen_charactors: + continue + + charactor_to_index[charactor] = index + seen_charactors.add(charactor) + + if charactor_to_index: + return next(iter(charactor_to_index.values())) + + return -1 + + +# @lc code=end diff --git a/problems/387.first-unique-character-in-a-string/step3.py b/problems/387.first-unique-character-in-a-string/step3.py new file mode 100644 index 0000000..8852c27 --- /dev/null +++ b/problems/387.first-unique-character-in-a-string/step3.py @@ -0,0 +1,30 @@ +# +# @lc app=leetcode id=387 lang=python3 +# +# [387] First Unique Character in a String +# + + +# @lc code=start +class Solution: + def firstUniqChar(self, s: str) -> int: + charactor_to_index = dict() + seen_charactors = set() + + for index, charactor in enumerate(s): + if charactor in charactor_to_index: + del charactor_to_index[charactor] + continue + if charactor in seen_charactors: + continue + + charactor_to_index[charactor] = index + seen_charactors.add(charactor) + + if charactor_to_index: + return next(iter(charactor_to_index.values())) + + return -1 + + +# @lc code=end