-
Notifications
You must be signed in to change notification settings - Fork 0
【Grind75Hard】1問目76. Minimum Window Substring #61
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,31 @@ | ||
| # ポインタを2つ使い条件を満たす範囲を見つける。 | ||
| # まずtの文字全てが含まれるwindowを見つけるために、rightを進めていく | ||
| # tの文字全てが含まれるwindowを見つけたら、leftを進めて無駄な文字を削除していく | ||
| class Solution: | ||
| def minWindow(self, s: str, t: str) -> str: | ||
| t_count = defaultdict(int) | ||
|
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. メモリのランダムアクセスを避け、定数倍高速化するため、 t_count = [0] * 128 としたいです。ですが、 t_count[ord(c)] += 1 をしなければならず、これはこれで微妙だなとも感じました。
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.
メモリが並んでいる方が 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. ListとHash Tableでメモリ上に各要素がどう配置されるかということだと思います。Listだと隣り合う要素はメモリ上も隣にあります。Hash Tableだとハッシュ関数で計算される位置に要素があるので、要素としての隣であってもメモリ場は隣になりです。 Hash Tableだと単純に目的の要素を探すのにハッシュ関数を実行する必要があるので、その分Listより定数倍遅くなりますし、あとは空間局所性が低いのでキャッシュのヒット率が下がります。
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. ありがとうございます。 |
||
| for c in t: | ||
| t_count[c] += 1 | ||
| window_count = defaultdict(int) | ||
|
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. こちらも |
||
| left = 0 | ||
| right = 0 | ||
| match_num = 0 | ||
|
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.
|
||
| min_length = math.inf, None, None | ||
| while right < len(s): | ||
| if s[right] in t_count: | ||
| window_count[s[right]] += 1 | ||
| if window_count[s[right]] == t_count[s[right]]: | ||
| match_num += 1 | ||
| while left <= right and match_num == len(t_count): | ||
| if min_length[0] > right - left: | ||
| min_length = right - left, left, right | ||
| if s[left] in t_count: | ||
| window_count[s[left]] -= 1 | ||
| if window_count[s[left]] < t_count[s[left]]: | ||
| match_num -= 1 | ||
| left += 1 | ||
| right += 1 | ||
| if min_length[0] == math.inf: | ||
| return "" | ||
| _, left, right = min_length | ||
| return s[left : right + 1] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| # rightをfor文で実装 | ||
| # 最小のwindowのindexのみを保持するように修正 | ||
| class Solution: | ||
| def minWindow(self, s: str, t: str) -> str: | ||
| t_count = defaultdict(int) | ||
| for c in t: | ||
| t_count[c] += 1 | ||
| left = 0 | ||
| match_num = 0 | ||
| window_count = defaultdict(int) | ||
| min_window_index = (left, math.inf) | ||
| for right, c in enumerate(s): | ||
| if c not in t_count: | ||
|
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. この if 文は入れても入れなくても良いように感じました。
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. 全文字管理してしまった方がシンプルで分かりやすいということですね。 |
||
| continue | ||
| window_count[c] += 1 | ||
| if window_count[c] == t_count[c]: | ||
| match_num += 1 | ||
| while left <= right and match_num == len(t_count): | ||
| if right - left + 1 < min_window_index[1] - min_window_index[0] + 1: | ||
|
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.
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. 長さを利用している部分も他にないので冗長な表現だなと考え直し、 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. defaultdict() の CPython の実装を考えるとよいと思います。 defaultdict はハッシュテーブルで実装されていると思います。 (間違っているかもしれないので調べていただけるとありがたいです。) ハッシュテーブルは、ある値にアクセスする際、はじめにキーのハッシュを調べます。続いてどのバケットに値が入っているか、インデックスを計算します。次にインデックスに基づいて、そのバケットにアクセスします。このアクセスはランダムアクセスとなります。そしてバケットから値への参照を用いて、値にアクセスします。このときにもう 1 回ランダムアクセスが発生します。このため、都合 2 回ランダムアクセスが発生します。 ただし、 Python のようなインタープリター言語では、上記は誤差になる、または諸々の理由により逆転する可能性があります。パフォーマンスに sensitive な場合は、上記まで考え、かつマイクロベンチマークをとり、比較検討するとよいと思います。そもそもパフォーマンスに sensitive な処理を Python で書くのが間違っている気もします。自分なら C++ で書くと思います。
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. ありがとうございます。 CPythonのソースとこちらのサイトからハッシュテーブルが利用されていることが確認できました。 |
||
| min_window_index = (left, right) | ||
| if s[left] in t_count: | ||
| window_count[s[left]] -= 1 | ||
| if window_count[s[left]] < t_count[s[left]]: | ||
| match_num -= 1 | ||
| left += 1 | ||
| min_window_left, min_window_right = min_window_index | ||
| if min_window_right == math.inf: | ||
| return "" | ||
| return s[min_window_left : min_window_right + 1] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| class Solution: | ||
| def minWindow(self, s: str, t: str) -> str: | ||
| t_count = defaultdict(int) | ||
| for c in t: | ||
| t_count[c] += 1 | ||
| left = 0 | ||
| min_window_index = (left, math.inf) | ||
|
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. 私なら、math.inf ではなくて、len(s) にするかもしれません。趣味の範囲です。 |
||
| window_count = defaultdict(int) | ||
| match_num = 0 | ||
| for right, c in enumerate(s): | ||
| if c not in t_count: | ||
| continue | ||
| window_count[c] += 1 | ||
| if window_count[c] == t_count[c]: | ||
| match_num += 1 | ||
| while left <= right and match_num == len(t_count): | ||
| if right - left + 1 < min_window_index[1] - min_window_index[0] + 1: | ||
| min_window_index = (left, right) | ||
| if s[left] in t_count: | ||
| window_count[s[left]] -= 1 | ||
| if window_count[s[left]] < t_count[s[left]]: | ||
| match_num -= 1 | ||
| left += 1 | ||
| left, right = min_window_index | ||
| if right == math.inf: | ||
| return "" | ||
| return s[left : right + 1] | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| # 変数名の修正 | ||
| # defaultdectを[0]*128に変更 | ||
| # tに含まれていない文字もカウントするように修正 | ||
| class Solution: | ||
| def minWindow(self, s: str, t: str) -> str: | ||
| num_chars_in_t = [0] * 128 | ||
| for c in t: | ||
| num_chars_in_t[ord(c)] += 1 | ||
| num_needed_matches = 128 - num_chars_in_t.count(0) | ||
| left = 0 | ||
| num_matches = 0 | ||
| num_chars_in_window = [0] * 128 | ||
| min_window_index = (left, len(s)) | ||
| for right, c in enumerate(s): | ||
| num_chars_in_window[ord(c)] += 1 | ||
| if num_chars_in_window[ord(c)] == num_chars_in_t[ord(c)]: | ||
| num_matches += 1 | ||
| while left <= right and num_matches == num_needed_matches: | ||
|
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.
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. おっしゃるとおりです。 |
||
| if right - left + 1 < min_window_index[1] - min_window_index[0] + 1: | ||
| min_window_index = (left, right) | ||
| num_chars_in_window[ord(s[left])] -= 1 | ||
| if num_chars_in_window[ord(s[left])] < num_chars_in_t[ord(s[left])]: | ||
| num_matches -= 1 | ||
| left += 1 | ||
| min_window_left, min_window_right = min_window_index | ||
| if min_window_right == len(s): | ||
| return "" | ||
| return s[min_window_left : min_window_right + 1] | ||
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.
t_countに何が含まれているのか、変数名からだけでは想起できませんでした。 char をどこかに入れてはいかがでしょうか?num_chars_in_tあたりが良いと思います。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.
ご指摘いただいた変数名の案は非常に分かりやすいです。
ありがとうございます。