Skip to content
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

Create ValidParenthesis.md #7

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
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
112 changes: 112 additions & 0 deletions ValidParenthesis.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# 20.valid parentheses

## すでに解いた方々(in:レビュー依頼 titleで検索)
- https://github.com/tarinaihitori/leetcode/pull/7/files
- https://github.com/katataku/leetcode/pull/6/files
- https://github.com/frinfo702/software-engineering-association/pull/9/files
- https://github.com/t0hsumi/leetcode/pull/6/files
- https://github.com/SuperHotDogCat/coding-interview/pull/41/files

## Step 1
### 考えたこと
- Pythonの文字列の操作とstackの使い方が怪しかったので早々に諦めてこれを確認。まずは素朴に場合分けしてみた。
- len(stack)は思いつかずにAraiさんの動画を確認

* 前回「一連の練習を終えるのに1問6時間かかってしまう」と悩んでおりましたが、標準出力を使えることを前回教えてもらったおかげで今回はStep1に20分、トータルでも2時間半くらいで済みました。つまらないことでお騒がせしてすいません…。

```Python
class Solution:
def isValid(self, s: str) -> bool:
stack = []

for char in s:
if char == "(" or char == "{" or char == "[":
stack.append(char)
elif stack and char == ")" and stack[-1] == "(":
stack.pop()
elif stack and char == "}" and stack[-1] == "{":
stack.pop()
elif stack and char == "]" and stack[-1] == "[":
stack.pop()
else:
print(stack)
return False

return len(stack) == 0
```

## Step 2
### 学んだこと
- 場合分けが多いので、continueや早期returnを用いていかに読みやすくするかが結構論じられている:https://github.com/tarinaihitori/leetcode/pull/7/files
- 辞書を使うやり方が場合分けが減らせてスマート
- Return len(stack)するよりimplicit falseを使う方流儀もあるが、趣味の範囲っぽいので周囲に合わせれば良いとのこと
- charが([{と一致するかはc in “([{”でも通るのは知らなかった: https://github.com/katataku/leetcode/pull/6/files#r1847770818
- 番兵を入れておくやり方、気になる:https://github.com/tarinaihitori/leetcode/pull/7/files#r1817714323
Copy link

@oda oda Jan 5, 2025

Choose a reason for hiding this comment

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

stack = ["*"]
bracket_pair = {"(": ")", "{": "}", "[": "]", "*": ""}
とでもしておくと not stack にならなくなります。
最後、"*" ひとつだけが残っているかで判別です。

Copy link
Owner Author

Choose a reason for hiding this comment

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

なるほど、こんなふうに書けるんですね。番兵について、先のLinkedListの問題で見ただけだったので、こんなことが出来ることすら想像できてませんでした。

class Solution:
    def isValid(self, s: str) -> bool:
        stack = [""]
        bracket_pairs = {"(": ")", "{": "}", "[": "]", "": ""}

        for char in s:
            if char in bracket_pairs:
                stack.append(char)
                continue
            if char != bracket_pairs[stack[-1]]:
                return False
            stack.pop()

        return len(stack) == 1

Copy link

Choose a reason for hiding this comment

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

私の意図は "*" でしたが、"" でも構いません。(斜体開始と解釈されて消えてしまいました。)

- 今回はListでpopとappendしたが、dequeを使えばstackの左側からも取り出したり追加したりできる: https://note.nkmk.me/python-collections-deque/
- チョムスキー階層、プッシュダウンオートマトンはよく分からないのでとりあえずスルー。こちらの方が関係ある箇所をまとめてくれていた:https://github.com/BumbuShoji/Leetcode/pull/7/files (後で読む:https://str.i.kyushu-u.ac.jp/~takeda/Lectures/FormalLanguageTheory2019/Resume/FormalLanguageTheory2019-11.pdf)
- 何が問題になっているのか理解が怪しいけれど、「双方向リストの機能を使っていないのにdequeを使うと無駄な処理が増えるので、O(1)同士でも違いがあることに敏感になろう」という話と理解した: https://github.com/BumbuShoji/Leetcode/pull/7/files#r1810557932
Copy link

Choose a reason for hiding this comment

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

速さの問題よりも、余計なことができるということは余計なことが必要であるという意図であろうと判断されながら読まれるということかと思います。

わざわざ「超限帰納法で」と書いたら「帰納法」では駄目な理由があるのだろうと解釈しながら読まれます。
通じないかしら。
「Python 3.1 で」と書いたら、他のバージョンでは動かないのかなと警戒して読ませることになるでしょう、とかでしょうか。

Copy link
Owner Author

Choose a reason for hiding this comment

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

よくわかりました、ありがとうございます。

- 一度辞書を使って書いてみよう

```Python
class Solution:
def isValid(self, s: str) -> bool:
bracket_pair = {"(": ")", "{": "}", "[": "]"}
stack = []

for char in s:
if char in bracket_pair:
stack.append(char)
continue
if not stack:
return False
if char != bracket_pair[stack[-1]]:
return False
stack.pop()

return len(stack) == 0

```

## Step 3
### コメント
- Step2で書いたIf文にcontinueを重ねるのが読みやすいが、頭からこれが書いていける感じがしない。。。一度別のコードを書いてから上の形に書き直す感じになりそうなので、自分にとって頭から自然に書ける書き方でしばらく書いてみたが、結果これまでと違ってStep3の書き方がなかなか1通りに収束しない。Step2の様なコードを頭から書きくだせる様な練習をすべきなのか…? -> 「if continueになれていない」というのが原因に思えるので、「常識」的な感覚に合わせるためにもstep2が自然に感じる様に練習する方がいい気がする。
- 5:15, 5:30, 4:10

```Python
def isValid(self, s: str) -> bool:
bracket_pairs = {"(": ")", "[": "]", "{": "}"}
stack = []

for char in s:
if char in bracket_pairs:
stack.append(char)
Copy link

Choose a reason for hiding this comment

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

continue を書く気持ちは、ここの行まで来たところで、「合流しない」と思っているかです。
つまり

if char in bracket_pairs:
    stack.append(char)
elif B:
    C
else:
    D
E

の E が存在しないと思っているならば、そうであることを頭の中に残しておかず、continue と書いてうっかり E を別の事情で書きたくなったときに間違えないようにしようという気持ちです。

Copy link
Owner Author

Choose a reason for hiding this comment

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

しっくり来ました、ありがとうございます。

elif char in bracket_pairs.values():
Copy link

Choose a reason for hiding this comment

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

sが入力条件を必ず満たすとする場合、ここはelseでいい様に思います(一個前のifでcharが「開きかっこ」かチェックしているため、ここでは必ず「閉じかっこ」=bracket_pairs.values()のどれかになります)。

if stack and char == bracket_pairs[stack[-1]]:
stack.pop()
else:
return False

return len(stack) == 0
```

## 悩んだけれど最終的にこっちで書いた
```Python
class Solution:
def isValid(self, s: str) -> bool:
bracket_pairs = {"(": ")", "[": "]", "{": "}"}
stack = []

for char in s:
if char in bracket_pairs:
stack.append(char)
continue
if not stack:
return False
if char != bracket_pairs[stack[-1]]:
return False
stack.pop()

return len(stack) == 0
Copy link

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

ループ中でif not stackを使っており、返り値でreturn len(stack) == 0としているので、少なくともコード内では統一しておくと良いと思います。

Copy link
Owner Author

Choose a reason for hiding this comment

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

len(stack)については好みレベルとのコメントを見かけたのでこちらを採用しましたが、一貫性と言われるとおっしゃる通りだなと思います。ありがとうございます。

Copy link

Choose a reason for hiding this comment

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

個人開発のときは従うも従わないも自由です。チームで動くときには、「私はこうしているが、こういう方法が広く使われていることは知っており、意見があれば柔軟に合わせるよ」くらい言えればいいでしょう。

```