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
154 changes: 154 additions & 0 deletions 20. Valid Parentheses.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
# [20. Valid Parentheses](https://leetcode.com/problems/valid-parentheses/description/)

## Step1

### 問題意図の考察
- Parentheses: such as (), [], {}, ([{}]) each parentheses should be closed by the same type of brackets.
- Every close bracket has a corresponding open bracket of the same type. LIFO (Last In, First Out), stack...

### 解法を考える
- stack (LIFO), and initialize an empty stack to keep track of opening brackets.
- Iterate through each character c in the string.
- (, [, { should be pushed into stack.
- if ), ], } came out, confirming the top of stack and corresponding.

### reference
- https://github.com/ryosuketc/leetcode_grind75/pull/2/commits/988f414dda681d6ee39cf76644fd7a3556a8a9d7#diff-826d80965446351c08bff958e457c5adffdf1803b61ea51422cc264a9e6288d4
-> step1 - step3 参考


```cpp
class Solution {
public:
bool isValid(string& s) {
unordered_map<char, char> open_to_close = {
{'(', ')'},
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

インデントがずれているようです。

{'{', '}'},
{'[', ']'}
};

stack<char> unmatched_open_brackets;
for (char c : s) {
if (open_to_close.contains(c)) {
unmatched_open_brackets.push(c);
continue;
}
if (unmatched_open_brackets.empty()) {
return false;
}
char expected_close = open_to_close[unmatched_open_brackets.top()];
if (c != expected_close) {
return false;
}
unmatched_open_brackets.pop();
}
return unmatched_open_brackets.empty();
}
};

```

## Step2
- https://en.cppreference.com/w/cpp/language/reference.html
 -> 参照渡し.

- キーと値のペアを保持するハッシュマップ: https://en.cppreference.com/w/cpp/container/unordered_map.html
-> キー:ここでは開き括弧 値:対応する閉じ括弧.

- https://google.github.io/styleguide/cppguide.html#Variable_Names
-> 変数名の確認。table_name: snake_case. lowerCamel.

- for (c : s) の文法に関して: https://en.cppreference.com/w/cpp/language/range-for.html
-> char c は文字列の s の各文字を1つずつ取り出して代入する。

- operator[]: unordered_map のキーに対応する値の取得に関して
-> https://en.cppreference.com/w/cpp/container/unordered_map/operator_at

別解をstep2にて。Chat-GPTに壁打ちして出したものなので、解法の1つとしてmemo.
- [期待するとじ括弧]を積むスタック(別解1)
->開き括弧を確認後、対応する閉じ括弧そのものをスタックに積む。閉じ括弧が来たらスタックの先頭と一致するかだけを見るので、マップも比較分岐も最小限で済ませる。

- [配列テーブル(closing→opening)+文字列をスタックとして使う] (別解2)
->unordered_map の代わりに 固定長配列(ASCII 128) を使って 閉じ括弧→対応する開き括弧 を O(1) 参照。スタックは std::string を使うと軽量(push/pop_back)。
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

スタックは std::string を使うと軽量(push/pop_back)。

この発想はありませんでした。ただ std::stack と比べて可読性が下がるように感じます。。

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

ほぼ変わらない気がします。> 軽量



```cpp
#include <string>
#include <stack>

//別解1
class Solution {
public:
bool isValid(const std::string& s) {
std::stack<char> st;

for (char c : s) {
switch (c) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

好みの範囲かもしれませんが、括弧の種類が増えたときの拡張性なども考えると括弧はmap等で管理し、ifで分岐処理を記述したほうが簡潔なように思います。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@maeken4
ご返信随分と遅くなり大変申し訳ありません。
ご指摘ありがとうございます。他のパターンを意識した記載ができておりませんでした。
問題文の範囲外でも一般的に考えられる所まで気をつけたいと思います。

case '(':
st.push(')');
break;
case '{':
st.push('}');
break;
case '[':
st.push(']');
break;
case ')':
case '}':
case ']':
if (st.empty() || st.top() != c) {
return false;
}
st.pop();
break;
default:
return false;
}
}
return st.empty();
}
};


```

```cpp
#include <string>
#include <array>

//別解2
class Solution {
class Solution {
public:
bool isValid(const std::string& s) {
if (s.size() % 2 != 0) {
return false;
}

std::array<char, 128> close_to_open{};
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

{} を付けると 0 で初期化されるのだと思いますが、以下で必要な要素に代入しているため、 {} を外して初期化なしにしてもよいと思いました。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@nodchip
ご返信が随分と遅くなり大変申し訳ございません。
ご指摘頂きありがとうございます。

ここでは、'(', '{', '['の3つだけなので、後々代入するのであれば、初期化なしにした方がいいかもしれないですね。
色々と調べた時に、未代入のインデックスにアクセスすると 未定義動作(UB)になる可能性がある(https://en.cppreference.com/w/cpp/language/ub.html)と記載があったので、初期化をしてました。

順に書いていった時に、必要・不必要なこと当たり前に書けるようにします。

close_to_open[')'] = '(';
close_to_open['}'] = '{';
close_to_open[']'] = '[';
Comment on lines +128 to +131
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

map ではなく array にしたのは意図的でしょうか。小さいものなら単純な array の方が効率がよい、というのはあるかもしれませんね。

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

@ryosuketc
ご返信が随分と遅くなり大変申し訳ございません。

こちらを書いた時は、「弧の種類(3種)が少なく、拡張を考えなくていいから、速さとシンプルさを優先して array を選んだ」形ですね。
新たに括弧を追加する場面も考えると、unordered_mapで書いた方がいいかもしれないですね。


std::string st;
st.reserve(s.size());

for (char c : s) {
if (c == '(' || c == '{' || c == '[') {
st.push_back(c);
} else {
if (st.empty() || st.back() != close_to_open[c]) {
return false;
}
st.pop_back();
}
}
return st.empty();
}
};


```

## Step3
30min