From 088fd9a76e405d34c923b1765e9eae19ec6e1f5e Mon Sep 17 00:00:00 2001 From: TrsmYsk <53941356+TrsmYsk@users.noreply.github.com> Date: Thu, 16 Oct 2025 19:34:06 +0900 Subject: [PATCH] Create 20. Valid Parentheses.md --- .../20. Valid Parentheses.md | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 20. Valid Parentheses/20. Valid Parentheses.md diff --git a/20. Valid Parentheses/20. Valid Parentheses.md b/20. Valid Parentheses/20. Valid Parentheses.md new file mode 100644 index 0000000..002c9ee --- /dev/null +++ b/20. Valid Parentheses/20. Valid Parentheses.md @@ -0,0 +1,144 @@ +問題文: https://leetcode.com/problems/valid-parentheses/description/ + +# step1: 何も見ないで書く(制限時間5分) +- CZAP4の計算モデルの回で聞いた問題なのでスタックを使うという方針はすぐに立てられた。 +- 手作業として考えるなら、こういう手順。 + - bracketの詰め合わせの中身がvalidかどうかをチェックしてください。 + - あなたの作業スペースにはbracketの種別ごとの対応表とopen置き場があります。 + - 詰め合わせの中身を前から順に取り出していき、open bracketが出てきたらopen置き場に積み重ねていってください。 + - close bracketが出たら、open置き場を確認してください。 + - open置き場が空だったら詰め合わせはvalidでないです。falseと報告してください。 + - open置き場が空じゃなければ一番上のbracketを取り出して対応表を確認し、対応が一致しているかを確認してください。一致していなければ詰め合わせはvalidではないので、falseと報告してください。 + - 一致していればそのbracketのペアは正しいです。正しいペアは脇に退けて、詰め合わせのチェックを続行してください。 + - 詰め合わせをすべてチェックし終えたとき、open置き場にbracketが残っていたら詰め合わせはvalidではありません。falseと報告してください。 + - 詰め合わせをチェックし終えて、open置き場に何もなければ詰め合わせはvalidです。trueを報告してください。 +- pythonでスタックを実装したいときはlist型が使える。\ https://docs.python.org/ja/3/tutorial/datastructures.html#more-on-lists + - リストが空のときにpop()メソッドを呼ぶとIndexErrorが送出される。事前にリストが空でないかのチェックが不可欠。 +- bracketの対応はdict型で保持する。\ https://docs.python.org/ja/3/library/stdtypes.html#typesmapping +- bracket以外の文字が入力に含まれているときは、何もせず`continue`する。 + - 実際の使用場面(コンパイラとか?)を想定するとブラケット以外の文字が含まれているほうが当たり前だと思われる。いちいち処理を止めるような実装にすると使い物にならない気がする。 +```python +class Solution: + def isValid(self, s: str) -> bool: + close_to_open_dict = {')' : '(', '}' : '{', ']' : '['} + open_list = list(close_to_open_dict.values()) + open_stack = [] + i = 0 + while i < len(s): + symbol = s[i] + if symbol in open_list: + open_stack.append(symbol) + i += 1 + continue + + if symbol in close_to_open_dict: + if len(open_stack) == 0: + return False + checking = open_stack.pop() + correct_open = close_to_open_dict[symbol] + if checking != correct_open: + return False + i += 1 + continue + i += 1 + + if len(open_stack) != 0: + return False + return True + +``` + + +# step2: 他の人を参考にコードを整える・コメントを予測する +## 直近のコード +https://github.com/hiroki-horiguchi-dev/leetcode/pull/6 +- 使用例を考えながら実装している点が素晴らしかった。 + +https://github.com/yas-2023/leetcode_arai60/pull/6 +- スタックだけでなく再帰も検討している。自分は再帰で解くという選択肢を思いつかなかった。 + + +https://github.com/nanae772/leetcode-arai60/pull/7 +- こちらもスタックと再帰。あとは、スタックからポップしたopen bracketをclose bracketに変換すればdictの初期化がわかりやすくなる(close_to_openからopen_to_closeにできる)との指摘あり。step2はこちらの方法を採用する。 + + + +## コメント集 +https://github.com/SanakoMeine/leetcode/pull/7#discussion_r1903350766 +- スタックの底に番兵を置くというテクニック。自分の選択肢に番兵が入っていないのは番兵を使ったことがないことが最大の原因なので、step2で使ってみる。 + +https://discord.com/channels/1084280443945353267/1225849404037009609/1231648833914802267 +- 想定外の入力への対応をどうするか。私は想定外の入力が来ても無視して`continue`することにしているが、状況によって対応は変わるらしい。自分以外の誰かが書き換えたときどうなるかという話は全然想定していなかった。 + + +https://github.com/HitoshiKoba/Arai60-public/pull/2#discussion_r1998179229 +- 進め方について。再帰下降構文解析の実装をするかどうか迷ってたが、先に進むことを優先しstep2,3ではやらない。step4とかでやります。 + +```python +class Solution: + def isValid(self, s: str) -> bool: + open_to_close = { + '(' : ')', + '{' : '}', + '[' : ']' + } + open_brackets = set(open_to_close.keys()) + close_brackets = set(open_to_close.values()) + valid_inputs = open_brackets.union(close_brackets) + stacked_opens = ['\0'] + open_to_close['\0'] = '\0' + + for symbol in s: + if symbol not in valid_inputs: + continue + if symbol in open_brackets: + stacked_opens.append(symbol) + continue + correspond_open = stacked_opens.pop() + correct_close = open_to_close[correspond_open] + if symbol != correct_close: + return False + + remaining = stacked_opens.pop() + if remaining != '\0': + return False + return True + +``` + + +# step3: 3回連続ミスなしで書く(制限時間15分) +- 制限時間を延長した。書き終わった後にタイポのチェックをすると時間が10分では足りなくなる。 +- 変数名を若干変更した。 +```python +class Solution: + def isValid(self, s: str) -> bool: + open_to_close = { + '(' : ')', + '{' : '}', + '[' : ']' + } + open_brackets = set(open_to_close.keys()) + close_brackets = set(open_to_close.values()) + valid_brackets = open_brackets.union(close_brackets) + sentinel = '\0' + unclosed_opens = [sentinel] + open_to_close[sentinel] = sentinel + + for symbol in s: + if symbol not in valid_brackets: + continue + if symbol in open_brackets: + unclosed_opens.append(symbol) + continue + latest_open = unclosed_opens.pop() + correct_close = open_to_close[latest_open] + if symbol != correct_close: + return False + + remaining = unclosed_opens.pop() + if remaining != sentinel: + return False + return True + +```