-
Notifications
You must be signed in to change notification settings - Fork 0
3. Longest Substring Without Repeating Characters #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,165 @@ | ||||||
| # 3. Longest Substring Without Repeating Characters | ||||||
|
|
||||||
| ## 1st | ||||||
|
|
||||||
| 注目している文字列に含まれる文字の集合を管理し、その集合に次の文字をいれるときに重複が出るなら重複が出なくなるまで左側を進めて集合から削除をしていく。 | ||||||
|
|
||||||
| 文字->indexの辞書じゃなくてsetで良かった... | ||||||
|
|
||||||
| left, rightだと閉区間っぽい気がして気になるが、左側を開区間にしたいときに伝わりやすい変数名はあるだろうか?もちろん閉区間にしてright - left + 1と区間の長さを計算してもいいのだが。 | ||||||
|
|
||||||
| `left = -1 # exclusive` みたいにとりあえずコメントを書くか。 | ||||||
|
|
||||||
| 所要時間: 8:16 | ||||||
|
|
||||||
| n: len(s) | ||||||
| - 時間計算量: O(n) | ||||||
| - 空間計算量: O(n) | ||||||
|
|
||||||
| ```py | ||||||
| class Solution: | ||||||
| def lengthOfLongestSubstring(self, s: str) -> int: | ||||||
| char_to_index = {} | ||||||
| max_length = 0 | ||||||
| left = -1 | ||||||
| for right in range(len(s)): | ||||||
| if s[right] in char_to_index: | ||||||
| while s[right] in char_to_index: | ||||||
| left += 1 | ||||||
| del char_to_index[s[left]] | ||||||
| max_length = max(max_length, right - left) | ||||||
| char_to_index[s[right]] = right | ||||||
| return max_length | ||||||
| ``` | ||||||
|
|
||||||
| setで書き直した。 | ||||||
|
|
||||||
| ```py | ||||||
| class Solution: | ||||||
| def lengthOfLongestSubstring(self, s: str) -> int: | ||||||
| chars = set() | ||||||
| left = -1 | ||||||
| max_length = 0 | ||||||
| for right in range(len(s)): | ||||||
| while s[right] in chars: | ||||||
| left += 1 | ||||||
| chars.remove(s[left]) | ||||||
| max_length = max(max_length, right - left) | ||||||
| chars.add(s[right]) | ||||||
| return max_length | ||||||
| ``` | ||||||
|
|
||||||
| ## 2nd | ||||||
|
|
||||||
| ### 参考 | ||||||
|
|
||||||
| - https://discord.com/channels/1084280443945353267/1196472827457589338/1246368719148548117 | ||||||
| - https://github.com/Mike0121/LeetCode/pull/21 | ||||||
|
|
||||||
| setってremove(), clear()だけじゃなく、discard()やpop()もあるのか。[Built-in Types — Python 3.12.4 documentation](https://docs.python.org/3.12/library/stdtypes.html#set) | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ドキュメント読むの大事ですね。 |
||||||
| discard(elem)はelemが無くてもエラーにならない、pop()は要素があるならelemを返す。 | ||||||
|
|
||||||
| 辞書にすると、管理にかかる計算コストが少し少なくなるのか。setにする方が素直だとは思うが。 | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 個人的には dict の方が while 文が不要で理解しやすく感じました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. なるほど...setの解法の場合、setの中身が注目している部分文字列の文字集合だという点が分かりやすいので好みなのですが、人それぞれなのかもですね There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
そうですね、人それぞれなのかもです。個人的には、各文字の index 情報があるのに利用しないのがもったいないという感覚がありました。 |
||||||
|
|
||||||
| ```py | ||||||
| class Solution: | ||||||
| def lengthOfLongestSubstring(self, s: str) -> int: | ||||||
| max_length = 0 | ||||||
| left = 0 | ||||||
| char_to_index = {} | ||||||
| for right in range(len(s)): | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 合計4回
Suggested change
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. enumerateや一次変数に入れるより、s[right]の方が文字列のright番目を取り出す意図がはっきりして分かりやすいかなと思って書いたんですが、うーむ好みの違いなのかもですね。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. たしかに元のコードのほうが可読性が高いのかもしれません。(僕もこの問題を解いたことがあったので)読み手というより書き手側の視点で見てしまっていたかもです。とくにもっと処理が大きくなったときには コードを書く側の目線で見たときに enumerate が楽だなと思ってのコメントでしたが、重要なのは読み手の視点なので上のコメントは適切でなかったかもです。 |
||||||
| if s[right] in char_to_index and char_to_index[s[right]] >= left: | ||||||
| left = char_to_index[s[right]] + 1 | ||||||
| max_length = max(max_length, right - left + 1) | ||||||
| char_to_index[s[right]] = right | ||||||
| return max_length | ||||||
| ``` | ||||||
|
|
||||||
| - https://discord.com/channels/1084280443945353267/1235971495696662578/1236898802703663208 | ||||||
| - https://github.com/sakzk/leetcode/pull/3 | ||||||
|
|
||||||
| 左端をloopで回す方法。 | ||||||
|
|
||||||
| ```py | ||||||
| class Solution: | ||||||
| def lengthOfLongestSubstring(self, s: str) -> int: | ||||||
| max_length = 0 | ||||||
| chars = set() | ||||||
| end = 0 | ||||||
| for begin in range(len(s)): | ||||||
| while end < len(s) and s[end] not in chars: | ||||||
| chars.add(s[end]) | ||||||
| end += 1 | ||||||
| max_length = max(max_length, end - begin) | ||||||
| if end == len(s): | ||||||
| break | ||||||
| chars.remove(s[begin]) | ||||||
| return max_length | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここはbegin, endに変更した理由が特になければ、step1, 2のようにleft, rightの方が読みやすく感じました。
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ループ不変条件が [begin, end) の区間に注目してるので半開区間であることを主張したく、begin-endを使ってみました。 |
||||||
| ``` | ||||||
|
|
||||||
| 全探索するならこうするだろうか。 | ||||||
|
|
||||||
| ```py | ||||||
| class Solution: | ||||||
| def lengthOfLongestSubstring(self, s: str) -> int: | ||||||
| max_length = 0 | ||||||
| for begin in range(len(s)): | ||||||
| for end in range(begin + 1, len(s) + 1): | ||||||
| if len(set(s[begin:end])) == end - begin: | ||||||
| max_length = max(max_length, end - begin) | ||||||
| return max_length | ||||||
| ``` | ||||||
|
|
||||||
| endの範囲がlen(s) + 1までにしなくてはいけないことに気づかず1ミス。 | ||||||
| TLEした。 | ||||||
|
|
||||||
| 毎回setを作るのがさすがに無駄かと思い、左端ごとにsetを作るようにする。こちらは通った。 | ||||||
|
|
||||||
| ```py | ||||||
| class Solution: | ||||||
| def lengthOfLongestSubstring(self, s: str) -> int: | ||||||
| max_length = 0 | ||||||
| for begin in range(len(s)): | ||||||
| chars = set() | ||||||
| end = begin | ||||||
| while end < len(s) and s[end] not in chars: | ||||||
| chars.add(s[end]) | ||||||
| end += 1 | ||||||
| max_length = max(max_length, end - begin) | ||||||
| return max_length | ||||||
| ``` | ||||||
|
|
||||||
| - https://discord.com/channels/1084280443945353267/1233295449985650688/1233598424780116039 | ||||||
| - https://github.com/Exzrgs/LeetCode/pull/2 | ||||||
| - https://discord.com/channels/1084280443945353267/1230079550923341835/1233450465560363070 | ||||||
| - https://github.com/thonda28/leetcode/pull/6 | ||||||
| - https://discord.com/channels/1084280443945353267/1226536545020809226/1228387150291144775 | ||||||
| - https://github.com/t0d4/leetcode/pull/2 | ||||||
| - https://discord.com/channels/1084280443945353267/1225849404037009609/1229038363613331498 | ||||||
| - https://github.com/SuperHotDogCat/coding-interview/pull/3 | ||||||
| - https://discord.com/channels/1084280443945353267/1226508154833993788/1228744387937177650 | ||||||
| - https://github.com/nittoco/leetcode/pull/3 | ||||||
| - https://discord.com/channels/1084280443945353267/1201211204547383386/1228003299651879044 | ||||||
| - https://github.com/shining-ai/leetcode/pull/48 | ||||||
| - https://discord.com/channels/1084280443945353267/1217527351890546789/1218817308328464405 | ||||||
| - https://github.com/cheeseNA/leetcode/pull/3 | ||||||
| - https://discord.com/channels/1084280443945353267/1200089668901937312/1215986234812403752 | ||||||
| - https://github.com/hayashi-ay/leetcode/pull/47 | ||||||
|
|
||||||
|
|
||||||
| ## 3rd | ||||||
|
|
||||||
| ```py | ||||||
| class Solution: | ||||||
| def lengthOfLongestSubstring(self, s: str) -> int: | ||||||
| max_length = 0 | ||||||
| chars = set() | ||||||
| left = -1 # exclusive | ||||||
| for right in range(len(s)): | ||||||
| while s[right] in chars: | ||||||
| left += 1 | ||||||
| chars.remove(s[left]) | ||||||
| max_length = max(max_length, right - left) | ||||||
| chars.add(s[right]) | ||||||
| return max_length | ||||||
| ``` | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
個人的には 2nd のように
left = 0から始める方が好きですが、left = -1から始めても良いと思います。