Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions leetcode/arai60/392_Is_Subsequence/memo.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# 392. Is Subsequence
* 問題リンク: https://leetcode.com/problems/is-subsequence/
* 言語: Python3

# Step1
## 解答の方針
* `t` の文字とインデックスの位置を保持するdict `key_to_index` を作成し、 `s` の先頭の文字から走査してインデックスがすべて小<大の順序を満たすかどうかを調べる方針

## すべてのテストケースを通過しないコード
```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
key_to_index = collections.OrderedDict()
for i, char_t in enumerate(t):
if key_to_index.get(char_t) is not None:
key_to_index.get(char_t).append(i)
else:
key_to_index[char_t] = [i]

print(key_to_index)

exists_char_i = []
for char_s in s:
if key_to_index.get(char_s) is None:
return False
elif len(key_to_index[char_s]) == 0:
return False
exists_char_i.append(key_to_index[char_s].pop(0))

for i in range(len(exists_char_i) - 1):
if exists_char_i[i] >= exists_char_i[i + 1]:
return False

return True
```

* しかし、`s` = `"ab"` 、 `t` = `"baab"` の場合、`OrderedDict([('b', [0, 3]), ('a', [1, 2])])` となって、誤って `False` を返す
* `s` のすべてのインデックスの順序を取得するアルゴリズムが思い浮かばず、正答を見た

## 正答
* `s` を基準に先頭の文字から調べていくのではなく、`t` を基準にして先頭から文字を取得し、取得した文字が `s` に存在すれば、検索中のSubsequenceの長さをインクリメントし、最終的に検索中のSubsequenceの長さと `s` の長さが一致するか判定

```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
if len(s) > len(t):
return False
if len(s) == 0:
return True

subseq_i = 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

自分なら t_i と対応させて s_i とすると思いました。

for t_i in range(0, len(t)):
if subseq_i < len(s):
if t[t_i] == s[subseq_i]:
subseq_i += 1

return subseq_i == len(s)
```

* 実行時間: 0ms
* メモリ使用量: 18.04MB
* 時間計算量: $O(n)$
* 空間計算量: $O(1)$

# Step2
## 他の方のコードを読む
* https://github.com/fuga-98/arai60/blob/27af9937ef75a7565d13d27e79853dd0f93fa070/392.%20Is%20Subsequence.md#step1
- ループの途中でreturnして、なるべく早く制御を戻している
* https://github.com/olsen-blue/Arai60/blob/0aac26e68fa6b8f516ff494da34578b8cc1cefc2/392.%20Is%20Subsequence.md#%E8%A7%A3%E6%B3%952%E4%BB%95%E4%BA%8B%E3%81%AE%E5%BC%95%E3%81%8D%E7%B6%99%E3%81%8E%E3%83%88%E3%83%83%E3%83%97%E3%83%80%E3%82%A6%E3%83%B3%E5%86%8D%E5%B8%B0dpac
- 解法2は再帰を使った方法。視線誘導がすっきりしていて読みやすい。スタックオーバーフローにならないか気になった。
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

再帰の深さの限界についてはこちらを見ておくとよさそうです。
https://docs.google.com/document/d/11HV35ADPo9QxJOpJQ24FcZvtvioli770WWdZZDaLOfg/edit?tab=t.0#heading=h.uvguf4c3q02d

- > while に掲げるものは主役であることが多いですね。
- これはもっともだと思った
* https://github.com/fhiyo/leetcode/blob/3bdbb38c39f00eba0213b5fb82d1cddd75e55537/392_is-subsequence.md
- ①はエッジケースがループに自然に組み込まれていていて読みやすい
- ②の正規表現を使う方法は面白いと思った
- ③は自分が当初やろうとしていた方針に近いかも。defaultdictを使ってリスト型で初期化し、リストを二分探索すればよかったのか…パズルに近いかも?
- ④は `find()` を使っているが、時間計算量が①と同じなのが意外だった
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

https://docs.python.org/3/library/stdtypes.html#str.find
公式ドキュメントを見ておきましょう。

* https://github.com/nittoco/leetcode/pull/16/files#diff-4213dd9e9bb2e47ed3241ed4250c36f5193149962dd86c6163c7d6b52b8b2668R71-R82
- `while True`で無限ループで解く方法

## 読みやすく整え直したコード
```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
idx = 0
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

単語から文字を削って変数名を付けた場合、読み手に取って認知負荷が上がる場合があります。原則フルスペルで付けることをお勧めいたします。

参考までにスタイルガイドへのリンクを貼ります。

https://google.github.io/styleguide/pyguide.html#316-naming

Avoid abbreviation. In particular, do not use abbreviations that are ambiguous or unfamiliar to readers outside your project, and do not abbreviate by deleting letters within a word.

ただし、上記のスタイルガイドは唯一絶対のルールではなく、複数あるスタイルガイドの一つに過ぎないということを念頭に置くことをお勧めします。また、所属するチームにより何が良いとされているかは変わります。自分の中で良い書き方の基準を持ちつつ、チームの平均的な書き方で書くことをお勧めいたします。

for char_t in t:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

chat_t という変数名は、 C++ の標準ライブラリの構造体名を連想させ、やや紛らわしく感じました。単に c または ch で十分だと思います。

if idx == len(s):
return True
if s[idx] == char_t:
idx += 1
return idx == len(s)
```
* 実行時間: 3ms
* メモリ使用量: 17.92MB
* 時間計算量: $O(n+m)$
* 空間計算量: $O(1)$

# Step3
```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
idx = 0
for char_t in t:
if idx == len(s):
return True
if s[idx] == char_t:
idx += 1
return idx == len(s)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

略語の使用についてはこちらを参照すると良いかもしれません。
https://discord.com/channels/1084280443945353267/1357969871673102446/1367455303582355496

```

* 解答時間
- 1回目: 1:05
- 2回目: 1:00
- 3回目: 1:03