-
Notifications
You must be signed in to change notification settings - Fork 0
392. Is Subsequence #52
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,81 @@ | ||
| import importlib.util | ||
| import random | ||
| import string | ||
| import time | ||
| from pathlib import Path | ||
|
|
||
| DIR = Path(__file__).resolve().parent | ||
|
|
||
|
|
||
| def load_module(name: str, path: Path): | ||
| spec = importlib.util.spec_from_file_location(name, path) | ||
| mod = importlib.util.module_from_spec(spec) | ||
| assert spec.loader is not None | ||
| spec.loader.exec_module(mod) | ||
| return mod | ||
|
|
||
|
|
||
| def is_sub(sol, s: str, t: str) -> bool: | ||
| return sol.isSubsequence(s, t) | ||
|
|
||
|
|
||
| def _ref_subsequence(s: str, t: str) -> bool: | ||
| if not s: | ||
| return True | ||
| i = 0 | ||
| for c in t: | ||
| if c == s[i]: | ||
| i += 1 | ||
| if i == len(s): | ||
| return True | ||
| return False | ||
|
|
||
|
|
||
| def _random_string(n: int) -> str: | ||
| return "".join(random.choices(string.ascii_lowercase, k=n)) | ||
|
|
||
|
|
||
| def main() -> None: | ||
| mod1 = load_module("sol1_impl", DIR / "sol1.py") | ||
| mod3 = load_module("sol3_impl", DIR / "sol3.py") | ||
| sol1 = mod1.Solution() | ||
| sol3 = mod3.Solution() | ||
|
|
||
| random.seed(0) | ||
| cases: list[tuple[str, str]] = [ | ||
| ("", "abc"), | ||
| ("abc", ""), | ||
| ("abc", "ahbgdc"), | ||
| ("axc", "ahbgdc"), | ||
| ("", ""), | ||
| ("a" * 100, "a" * 200 + "b" * 200), | ||
| ] | ||
| for _ in range(200): | ||
| la, lb = random.randint(0, 500), random.randint(0, 2000) | ||
| cases.append((_random_string(la), _random_string(lb))) | ||
|
|
||
| for s, t in cases: | ||
| is_sub(sol1, s, t) | ||
| is_sub(sol3, s, t) | ||
|
|
||
| reps = 50 | ||
| t0 = time.perf_counter() | ||
| for _ in range(reps): | ||
| for s, t in cases: | ||
| is_sub(sol1, s, t) | ||
| t_sol1 = time.perf_counter() - t0 | ||
|
|
||
| t0 = time.perf_counter() | ||
| for _ in range(reps): | ||
| for s, t in cases: | ||
| is_sub(sol3, s, t) | ||
| t_sol3 = time.perf_counter() - t0 | ||
|
|
||
| print(f"sol1: {t_sol1 * 1000:.3f} ms") | ||
| print(f"sol3: {t_sol3 * 1000:.3f} ms") | ||
| ratio = t_sol3 / t_sol1 | ||
| print(f"(sol3 / sol1: {ratio:.3f})") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| # 392. Is Subsequence | ||
|
|
||
| sとtに対してそれぞれindexを保存して最後まで一致させられるかどうかを判定する: sol1.py | ||
| - 時間計算量 O(|t|)、空間計算量 O(1) | ||
|
|
||
| 書き終わって early return が可能なことに気が付く。index の取り方も変えた: sol2.py | ||
| indexの取り方はもとのままの場合にも書く: sol3.py 添字の取り方が分かりやすいのでこちらの方が良さそう | ||
| - 時間計算量 O(|t|)、空間計算量 O(1) | ||
| - 漸近計算量では差がないが、early returnがある影響でこちらの方が早い | ||
|
|
||
| https://discord.com/channels/1084280443945353267/1201211204547383386/1231637671831408821 | ||
|
|
||
| > これは、正規表現で s の文字のすべての間に .* を挟み込んで、マッチすればいいので、一回舐めれば解けそうですね | ||
|
|
||
| > これは、本当はエスケープしないといけない | ||
|
|
||
| 正規表現を使ってかいてみる: sol4.py | ||
|
|
||
| https://discord.com/channels/1084280443945353267/1225849404037009609/1243290893671465080 | ||
|
|
||
| 関数型として書き、ループに直している | ||
| 再帰で書いてみる: sol5.py | ||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
| ### 追記 | ||
| sol3.pyとsol1.pyの速度はそれほど変わらず、sol3.pyの方が遅い場合もあった | ||
|
|
||
| sol1: 1532.053 ms | ||
| sol3: 1710.168 ms | ||
| (sol3 / sol1: 1.116) |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| class Solution: | ||
| def isSubsequence(self, s: str, t: str) -> bool: | ||
| if not s: | ||
| return True | ||
| if not t: | ||
| return False | ||
|
|
||
| index_s = 0 | ||
| index_t = 0 | ||
| while index_t < len(t): | ||
| if s[index_s] == t[index_t]: | ||
| index_s += 1 | ||
| if index_s == len(s): | ||
| return True | ||
| index_t += 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. 毎回のiterationで、+1するので、forループで書き換えた方が良さそうです。
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. なるほど、どちらもご指摘の通りです。自分でも見直してこうした点に気づけるようになりたいです。 |
||
|
|
||
| return False | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| class Solution: | ||
| def isSubsequence(self, s: str, t: str) -> bool: | ||
| if not s: | ||
| return True | ||
| if not t: | ||
| return False | ||
|
|
||
| index_s = 0 | ||
| for index_t in range(len(t)): | ||
| if s[index_s] == t[index_t]: | ||
| index_s += 1 | ||
| if index_s == len(s): | ||
| return True | ||
|
|
||
| return False |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| class Solution: | ||
| def isSubsequence(self, s: str, t: str) -> bool: | ||
| if not s: | ||
| return True | ||
| if not t: | ||
| return False | ||
|
|
||
| remaining_num_s = len(s) | ||
| remaining_num_t = len(t) | ||
| while remaining_num_s <= remaining_num_t: | ||
| if s[len(s) - remaining_num_s] == t[len(t) - remaining_num_t]: | ||
| remaining_num_s -= 1 | ||
| if remaining_num_s == 0: | ||
| return True | ||
| remaining_num_t -= 1 | ||
|
|
||
| return False |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,17 @@ | ||
| class Solution: | ||
| def isSubsequence(self, s: str, t: str) -> bool: | ||
| if not s: | ||
| return True | ||
| if not t: | ||
| return False | ||
|
|
||
| index_s = 0 | ||
| index_t = 0 | ||
| while len(s) - index_s <= len(t) - index_t: | ||
|
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. 制約を見ると、sは短めなので、early returnするメリットはあまりないかもしれませんね。
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. 速度を簡単に測ってみましたが大きな差はなく sol3.pyの方が遅い場合もありました。 sol1: 1532.053 ms |
||
| if s[index_s] == t[index_t]: | ||
| index_s += 1 | ||
| if index_s == len(s): | ||
| return True | ||
| index_t += 1 | ||
|
|
||
| return False | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| import re | ||
|
|
||
|
|
||
| class Solution: | ||
| def isSubsequence(self, s: str, t: str) -> bool: | ||
| pattern = "" | ||
| for c in s: | ||
| pattern += ".*" + re.escape(c) | ||
| return re.match(pattern, t) is not None |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| class Solution: | ||
| def isSubsequence(self, s: str, t: str) -> bool: | ||
| if len(s) == 0: | ||
| return True | ||
| if len(s) > len(t) or len(t) == 0: | ||
| return False | ||
| if s[0] == t[0]: | ||
| return self.isSubsequence(s[1:], t[1:]) | ||
| return self.isSubsequence(s, t[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.
これは、index_sをインクリメントした時以外は判定不要なので、上のifの中に入れた方が良さそうです。