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
100 changes: 100 additions & 0 deletions Python3/392. Is Subsequence.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
## Step 1. Initial Solution

- 最初に思い付いたのは subsequence の文字列を一つずつ見て行って、元文字列を前から線形に探索していく方法
- 特に整序されているわけではないのでこの順番で見ていくのが良さそう
- 毎度文字列のコピーをするのは避けたいのでindexを動かしていく
- indexを動かしながら見つからない時の処理をする
- 本当はfind関数の方が最適化されていて速そうだが引数の順番とかを思い出せなかったのでfor文で実装

```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
last_char_index = 0
char_index = 0
for c in s:
for i in range(last_char_index, len(t)):
if c == t[i]:
char_index = i + 1
break
if last_char_index == char_index:
return False
last_char_index = char_index
return True
```

Choose a reason for hiding this comment

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

last_char_index と char_index が少し把握しにくかったです。char_index はあるループで一致する文字が見つかったかを判定するために使われていると思います。
フラグ的にするか

    def isSubsequence(self, s: str, t: str) -> bool:
        last_char_index = 0
        for c in s:
            char_index = last_char_index
            for i in range(last_char_index, len(t)):
                if c == t[i]:
                    last_char_index = i + 1
                    break
            if last_char_index == char_index:
                return False
        return True

for-else を使うことも可能かと思いました。

    def isSubsequence(self, s: str, t: str) -> bool:
        search_start_index = 0
        for c in s:
            for i in range(search_start_index, len(t)):
                if c == t[i]:
                    search_start_index = i + 1
                    break
            else:
                return False
        return True


- str.find(ch, index_from)のような形だった気がすると思って実装したら行けた

```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
char_index = 0
for c in s:
char_index = t.find(c, char_index) + 1
if char_index == 0:
Copy link

Choose a reason for hiding this comment

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

find() から -1 が返ってきたときに、 t.find(c, char_index) + 1 が 0 になるから return False するというのは、ややパズルに感じました。

char_index = t.find(c, char_inde)
if char_index == -1:
    return False
char_index += 1

のほうが分かりやすいと思います。

return False
return True
```

### Complexity Analysis

- 時間計算量:O(n)
- n: tの長さ (=10^4)より10^8 Step/sならk*0.1ms
- 空間計算量:O(1)

## Step 2. Alternatives

- Follow upの「もし沢山の subsequences を確認するならどうするか」という点については、tから辞書を作って参照しながら進める方が速そう
- ただ、高々26分割して探せるだけなので大きな高速化には至らないか

```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
char_to_indices = defaultdict(list)
for i in range(len(t)):
char_to_indices[t[i]].append(i)

char_to_next_index = {}
for ch in string.ascii_lowercase:
char_to_next_index[ch] = 0

last_index = -1
for ch in s:
char_indices = char_to_indices[ch]
while True:
if char_to_next_index[ch] >= len(char_indices):
return False

if char_indices[char_to_next_index[ch]] > last_index:
last_index = char_indices[char_to_next_index[ch]]
break
char_to_next_index[ch] += 1
return True
```

- https://github.com/tokuhirat/LeetCode/pull/57/files#diff-56d94b18dcde3860786d1548afc303826c1dd1b5174a1c5baf29ccdac65c935eR30-R42
- シンプルに書くとこのようなindexをそれぞれで動かしていく方法もあり
- https://github.com/ryosuketc/leetcode_arai60/pull/57/files#diff-d4bf0ffef278eb91530487cdf19578095c7402d9ba6698ca2a48c8c9e32cffe9R12
- subsequence … can be none のところを自分もちゃんと意識していなかったので気を付けたい
- https://github.com/fhiyo/leetcode/pull/55/files#diff-a6c7d5ff748fd033529b0b0a550ed2aa570e18edc3e2c61da5094aec0e23a91eR45
- follow upについて、基本的な考え方は似ている
- bisectで探す方法は考えていなかった
- 確かにindexをlast_indexから探すのはあり
- そもそも一致する可能性があまりないものを二分探索で探す発想がなかった
- 昇順になっていることを考えると計算量的には二分探索の方が良さそうだが自分のやり方のように最後に見た場所から探し始める方がHeuristicな考えとして有効な気がしなくもない
- 最後に見た場所をbisectのloに入れておくというのもありか?
- この問題に対しては処理を複雑にし過ぎているような気もする

## Step 3. Final Solution

- 自分で最初に書いたやり方がしっくり来ていたのでちょっとだけ変えて練習
Copy link

Choose a reason for hiding this comment

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

計算量的にはO(文字の種類*文字列の長さ)になるので状況によっては悪いですが、find がネイティブなので文字の種類が少なければ速いだろうという見積もりまであれば OK です。


```python
class Solution:
def isSubsequence(self, s: str, t: str) -> bool:
char_index = -1
for ch in s:
char_index = t.find(ch, char_index + 1)
if char_index == -1:
return False
return True
```