-
Notifications
You must be signed in to change notification settings - Fork 0
22. Generate Parentheses #6
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
Merged
Merged
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,32 @@ | ||
| class Solution { | ||
| // 再帰 | ||
| // ACだが、途中でメモ化をしてないのでかなり計算量が悪くなってTLEになった | ||
| public List<String> generateParenthesis(int n) { | ||
| Set<String> resultParenetheses = new HashSet<>(); | ||
| Set<String> pastState = new HashSet<>(); | ||
| insertParenetheses(n, new StringBuilder(), resultParenetheses, pastState); | ||
| return new ArrayList<>(resultParenetheses); | ||
| } | ||
|
|
||
|
|
||
| private void insertParenetheses(int n, StringBuilder current, Set<String> resultParenetheses, | ||
| Set<String> pastState) { | ||
| if (n == 0) { | ||
| resultParenetheses.add(new String(current)); | ||
| return; | ||
| } | ||
| if (pastState.contains(new String(current))) { | ||
| return; | ||
| } | ||
| pastState.add(new String(current)); | ||
| for (int i = 0; i <= current.length(); i++) { | ||
| current.insert(i, "("); | ||
| for (int j = i + 1; j <= current.length(); j++) { | ||
| current.insert(j, ")"); | ||
| insertParenetheses(n - 1, current, resultParenetheses, pastState); | ||
| current.delete(j, j + 1); | ||
| } | ||
| current.delete(i, i + 1); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,35 @@ | ||
| # firstSolution | ||
|
|
||
| アプローチとしては()を再帰的に適切な位置に挿入していき、最終的な長さになったら HashSet に加えていくというもの(重複を防ぐために Set を使っています)。 | ||
| 適切な位置というのは、挿入した"("に対して、"("の位置より右側で")"が挿入されることをいみしています。(これで正しい文字列であることは保証されるので) | ||
| 途中でメモ化していないことによる TLE になったので、途中の文字列についても保持して重複した再帰処理を防ぐようにしました・ | ||
|
|
||
| # refactoredByMyselfSolution | ||
|
|
||
| # refactoredSolution | ||
|
|
||
| 0, 変数名修正 | ||
| コメントいただいた箇所について修正しました。 | ||
| 状態についても必要十分で変数名に含めてあげる意識が足りないなと思いました。 | ||
|
|
||
| 1, Queue を使った loop | ||
| コメントいただいた方法を元に以下のように考えました。 | ||
| アプローチとしては、末尾に"(",")"を挿入して Queue に保存していき、最終的に文字列の長さが 2 _ n になったら、正しい文字列かどうかを確認して正しければ List に加えるというものです。 | ||
| 途中で正しい文字列か(")"のが多くならないか)をチェックしています。こちらについては最後だけ正しい文字列かをチェックする方法もあり、処理のシンプルさでは最後だけチェックのがよいですが、速度的に n が大きくなればなるほど最終的にチェックする数が増えると思うので、途中で判定して Queue に追加するものを選択したほうがはやくなると思っています。 | ||
| 途中でコメントもいただき、左の数を文字列に対して保持しておけば、毎回チェックの処理を O(n)で走らせないですむので、新規に(の数を保持しておく Queue を作りました。 | ||
| 時間計算量がわかりやすくて、文字列の長さ分毎回2倍増えていきますので、2^(2n)かかり、あと正しいかの判定に 2n かかるので、O(2^(2n) _ n)となるかと思います。 | ||
| 空間計算量についても、Queue に入る最大の要素数は 2^(2n - 1), 要素となる文字列の長さは最大 2n なので O(2^(2n) \* n)となるかとおもいます。 | ||
|
|
||
| 追加いただいたコメントで、Queue は1つにまとました(管理の面でも、複数管理の場合は更新し忘れなどのリスクもあるのでまとめて管理したほうが合理的だと思いました。) | ||
| 文字列と開いた(の数をまとめたクラスの命名については少し不安です(State とか Info とかしか浮かびませんでした)。 | ||
|
|
||
| 2, 再帰を使った方法 | ||
| 再帰については、基本的に上記と考え方が同じなので、すんなり実装はできました。コメントいただいた通り、途中で条件をしっかり確認すればかなり不要な文字列のチェックを抑えることができるので、しっかり実装前や途中で不要なパターンをしっかり明確化できるのかが大事だと改めて感じました。 | ||
| また String の add に文字数分のコストがかかることを教えていただき、かなり処理時間に差が出るので StringBuilder を使ったアプローチを常に考えようと思いました。 | ||
| [確認した他の実装](https://github.com/hayashi-ay/leetcode/pull/70) | ||
|
|
||
| 3, Queue を使った loop の別アプローチ | ||
| 複数のアプローチを思い浮かべられるとよい+ループの考えについて教えていただいたので、それをベースに違うアプローチで()を挿入した。 | ||
| 挿入については"(" + ")"\*n n:0 <= n <= 閉じてない(の個数 として挿入をしていく | ||
| 挿入したものは被りはない | ||
| 挿入中に n = 4 で((((((((( みたいにケースでは閉じる括弧を加えると溢れてしまうケースがありえるので、それを途中で阻止するようにした。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| class Solution { | ||
| public List<String> generateParenthesis(int n) { | ||
| List<String> parenthesisStrings = new ArrayList<>(); | ||
| Queue<String> parenthesisCandidate = new LinkedList<>(); | ||
| parenthesisCandidate.add(""); | ||
|
|
||
| while (!parenthesisCandidate.isEmpty()) { | ||
| String current = parenthesisCandidate.poll(); | ||
| if (!isValidParenethesisInProcess(current)) { | ||
| continue; | ||
| } | ||
| if (current.length() == 2 * n) { | ||
| if (isValidParenethesis(current)) { | ||
| parenthesisStrings.add(current); | ||
| } | ||
| continue; | ||
| } | ||
| parenthesisCandidate.add(current + "("); | ||
| parenthesisCandidate.add(current + ")"); | ||
| } | ||
| return parenthesisStrings; | ||
| } | ||
|
|
||
| private boolean isValidParenethesisInProcess(String string) { | ||
| int leftParenethesisCount = 0; | ||
| for (char c : string.toCharArray()) { | ||
| if (c == '(') { | ||
| leftParenethesisCount++; | ||
| } else { | ||
| leftParenethesisCount--; | ||
| } | ||
|
|
||
| if (leftParenethesisCount < 0) { | ||
| return false; | ||
| } | ||
| } | ||
| return leftParenethesisCount >= 0; | ||
| } | ||
|
|
||
| private boolean isValidParenethesis(String string) { | ||
| int leftParenethesisCount = 0; | ||
| for (char c : string.toCharArray()) { | ||
| if (c == '(') { | ||
| leftParenethesisCount++; | ||
| } else { | ||
| leftParenethesisCount--; | ||
| } | ||
|
|
||
| if (leftParenethesisCount < 0) { | ||
| return false; | ||
| } | ||
| } | ||
| return leftParenethesisCount == 0; | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| class Solution { | ||
| public List<String> generateParenthesis(int n) { | ||
| List<String> allParentheses = new ArrayList<>(); | ||
| Queue<String> parenthesisCandidate = new LinkedList<>(); | ||
| parenthesisCandidate.add(""); | ||
|
|
||
| while (!parenthesisCandidate.isEmpty()) { | ||
| String current = parenthesisCandidate.poll(); | ||
| if (!isValidParenethesisInProcess(current)) { | ||
| continue; | ||
| } | ||
| if (current.length() == 2 * n) { | ||
| if (isValidParenethesis(current)) { | ||
| parenthesisStrings.add(current); | ||
| } | ||
| continue; | ||
| } | ||
| parenthesisCandidate.add(current + "("); | ||
| parenthesisCandidate.add(current + ")"); | ||
| } | ||
| return parenthesisStrings; | ||
| } | ||
|
|
||
| private boolean isValidParenethesisInProcess(String string) { | ||
| int notClosedLeftParenethesisCount = 0; | ||
| for (char c : string.toCharArray()) { | ||
| if (c == '(') { | ||
| notClosedLeftParenethesisCount++; | ||
| } else { | ||
| notClosedLeftParenethesisCount--; | ||
| } | ||
|
|
||
| if (notClosedLeftParenethesisCount < 0) { | ||
| return false; | ||
| } | ||
| } | ||
| return notClosedLeftParenethesisCount >= 0; | ||
| } | ||
|
|
||
| private boolean isValidParenethesis(String string) { | ||
| int notClosedLeftParenethesisCount = 0; | ||
| for (char c : string.toCharArray()) { | ||
| if (c == '(') { | ||
| notClosedLeftParenethesisCount++; | ||
| } else { | ||
| notClosedLeftParenethesisCount--; | ||
| } | ||
|
|
||
| if (notClosedLeftParenethesisCount < 0) { | ||
| return false; | ||
| } | ||
| } | ||
| return notClosedLeftParenethesisCount == 0; | ||
| } | ||
|
|
||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| class Solution { | ||
| public List<String> generateParenthesis(int n) { | ||
| List<String> allParentheses = new ArrayList<>(); | ||
| Queue<ParenthesisState> parenthesisCandidate = new LinkedList<>(); | ||
| parenthesisCandidate.add(new ParenthesisState("", 0)); | ||
|
|
||
| while (!parenthesisCandidate.isEmpty()) { | ||
| ParenthesisState currentState = parenthesisCandidate.poll(); | ||
| String curerntString = currentState.str; | ||
| int numCurrentUnClosedLeftParenthesis = currentState.numUnclosedLeftParenthesis; | ||
|
|
||
| if (curerntString.length() == 2 * n) { | ||
| allParentheses.add(currentState.str); | ||
| continue; | ||
| } | ||
| if (numCurrentUnClosedLeftParenthesis < 2 * n - curerntString.length()) { | ||
| parenthesisCandidate.add(new ParenthesisState(curerntString + "(", | ||
| numCurrentUnClosedLeftParenthesis + 1)); | ||
| } | ||
| if (0 < numCurrentUnClosedLeftParenthesis) { | ||
| parenthesisCandidate.add(new ParenthesisState(curerntString + ")", | ||
| numCurrentUnClosedLeftParenthesis - 1)); | ||
| } | ||
| } | ||
| return allParentheses; | ||
| } | ||
|
|
||
| class ParenthesisState { | ||
| String str; | ||
| int numUnclosedLeftParenthesis; | ||
|
|
||
| ParenthesisState(String str, int numUnclosedLeftParenthesis) { | ||
| this.str = str; | ||
| this.numUnclosedLeftParenthesis = numUnclosedLeftParenthesis; | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| class Solution { | ||
| public List<String> generateParenthesis(int n) { | ||
| List<String> allParentheses = new ArrayList<>(); | ||
| makeCombination(n, allParentheses, new StringBuilder(), 0); | ||
| return allParentheses; | ||
| } | ||
|
|
||
| private void makeCombination(int n, List<String> allParentheses, StringBuilder current, | ||
| int unclosedLeftParentheses) { | ||
| if (current.length() == 2 * n) { | ||
| allParentheses.add(current.toString()); | ||
| return; | ||
| } | ||
| // 残りの挿入可能文字数が、閉じていない(の数より多い時は(を挿入 | ||
| if (unclosedLeftParentheses < 2 * n - current.length()) { | ||
| current.append("("); | ||
| makeCombination(n, allParentheses, current, unclosedLeftParentheses + 1); | ||
| current.deleteCharAt(current.length() - 1); | ||
| } | ||
| // 閉じていない(があるので)を挿入 | ||
| if (0 < unclosedLeftParentheses) { | ||
| current.append(")"); | ||
| makeCombination(n, allParentheses, current, unclosedLeftParentheses - 1); | ||
| current.deleteCharAt(current.length() - 1); | ||
| } | ||
| } | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| class Solution { | ||
| public List<String> generateParenthesis(int n) { | ||
| List<String> allParentheses = new ArrayList<>(); | ||
| Queue<Pair<String, Integer>> parenthesisCandidate = new LinkedList<>(); | ||
| parenthesisCandidate.add(new Pair<>("", 0)); | ||
|
|
||
| while (!parenthesisCandidate.isEmpty()) { | ||
| Pair<String, Integer> currentState = parenthesisCandidate.poll(); | ||
| String curerntString = currentState.getKey(); | ||
| int numCurrentUnClosedLeftParenthesis = currentState.getValue(); | ||
|
|
||
| if (curerntString.length() == 2 * n && numCurrentUnClosedLeftParenthesis == 0) { | ||
| allParentheses.add(currentState.getKey()); | ||
| continue; | ||
| } | ||
| for (int i = 0; i <= numCurrentUnClosedLeftParenthesis + 1; i++) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 「)」をまとめて複数個追加するメリットはないように感じます。「(」または「)」を 1 文字ずつ追加すれば十分だと思います。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (これは、私が試しにお願いしてみた解法です。) |
||
| if (2 * n < curerntString.length() + 1 + i) { | ||
| continue; | ||
| } | ||
| String closeParenthesis = ")".repeat(i); | ||
| String nextCandidate = curerntString + "(" + closeParenthesis; | ||
| parenthesisCandidate.add(new Pair<String, Integer>(nextCandidate, | ||
| numCurrentUnClosedLeftParenthesis + 1 - i)); | ||
| } | ||
| } | ||
| return allParentheses; | ||
| } | ||
| } | ||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pair は、コードを読むにあたり、中に含まれている値を把握するのに認知負荷がかかるため、あまり使わないほうが良いと思います。代わりにユーザー定義クラスを使ったほうが良いと思います。
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(これは、私が試しにお願いしてみた解法です。)