|
1 | 1 | # [Problem 1190: Reverse Substrings Between Each Pair of Parentheses](https://leetcode.com/problems/reverse-substrings-between-each-pair-of-parentheses/description/?envType=daily-question)
|
2 | 2 |
|
3 | 3 | ## Initial thoughts (stream-of-consciousness)
|
| 4 | +- The simplest solution would be to use recursion: |
| 5 | + - Start with `result = ''` |
| 6 | + - Append characters to `result` until if/when we encounter an open parenthesis (at position `i`) |
| 7 | + - If so, set `counter = 1` |
| 8 | + - Now continue to move forward through the string (starting at `i + 1`): |
| 9 | + - If we encounter an open parenthesis, `counter += 1` |
| 10 | + - If we encounter a closed parenthesis, `counter -= 1` |
| 11 | + - If we ever get to `counter == 0` (at position `j`): |
| 12 | + - set `result += reverseParenthesis(s[i + 1:j])[::-1]` |
| 13 | + - set `i = j + 1` |
| 14 | + - continue |
| 15 | + - Then once we run out of characters, return `result` |
| 16 | +- I can see that the recursive solution will "work," but it's inefficient. E.g., the string can be 2000 characters long, so if it's something like `s = (((((....((()))...)))))` we'll need to potentially recurse to a depth of up to 1000, which would be (a) memory inefficient (we have to copy all of the variables with each new recurse), and (b) potentially beyond the recursion limit. |
| 17 | +- Another approach would be to maintain two "result" strings-- one for the starting characters (moving from the beginning to the end) and the other for the ending characters (moving from the end to the beginning) |
| 18 | +- Or...maybe we can just use two indices (`i` moves from beginning to end and `j` moves from the end to the beginning)? |
| 19 | + - We could do something like: |
| 20 | + - Keep track of whether we need to reverse (parenthesis depth is odd) or not (parenthesis depth is even). Start with `reverse = False`, `i = 0`, `j = len(s) - 1`, `result = ''`, |
| 21 | + - While `i < j`: |
| 22 | + - simple case: we're at an even depth (`reverse` is `False`): just append each character in turn to `result` and increment `i` by 1 until we hit an open parenthesis |
| 23 | + - complicated: if we're at an odd depth, i think we need to do something like: |
| 24 | + - move `i` forward *and* `j` backwards, keep track of stuff in the forward direction in one variable and in the backwards direction in another variable. We'll want to prepend each new character `s[i]` to the forward direction string and append each new character `s[j]` to the backwards direction string |
| 25 | + - if we hit an open parenthesis in the forward direction we stop moving forward, and if we hit a backwards parenthesis in the backwards direction we stop moving backwards |
| 26 | + - to figure out: what do we *do* with those temporary strings... |
| 27 | + - I guess we could append the backwards direction string to `result` immediately...but what happens to the forward direction string 🤔? Kind of tricky! |
| 28 | +- Hmm... |
| 29 | +- Could we do an "in place" reverse? |
| 30 | + - Look for the outermost open parenthesis |
| 31 | + - Look for the matching close parenthesis |
| 32 | + - Keep the stuff outside those outermost parentheses |
| 33 | + - Remove the parentheses + reverse the stuff inside them (now the string shrinks in length by 2...maybe this isn't terrible?) |
| 34 | + - Would this work... 🤔... |
| 35 | + - Suppose we had something like `s = 'a(bc(de)fg)h'`. What happens? |
| 36 | + - We have parentheses: |
| 37 | + - `before = 'a'`; `after = 'h'`; `to_reverse = 'bc(de)fg` |
| 38 | + - Now set `s = before + to_reverse[::-1] + after` -- so `s` is now `'agf)ed(cbh'`...so clearly we'll need to match *both* open and closed parentheses. If the parentheses weren't guaranteed to be balanced, this would be a problem. Actually, maybe when we reverse things we can just flip the directions of the parentheses? That'd give us `'agf(ed)cbh'`... |
| 39 | + - There are still parentheses remaining, so now: |
| 40 | + - `before = 'agf'`; `after = 'cbh'`; `to_reverse = 'ed'` |
| 41 | + - Now we set `s = before + to_reverse[::-1].replace('(', '*').replace(')', '(').replace('*', ')') + after` (aside: this is a mess...), so `s = 'agfdecbh'`. Which seems...correct... |
| 42 | + - Is this efficient? |
| 43 | + - Potentially involves reshuffling stuff lots of times in memory, which seems sub-optimal, but maybe it's ok? |
| 44 | + - The solution is $O(np)$ where $n$ is the string length and $p$ is the number of parenthesis pairs. If $n = 2000, p = 1000$ that could be slow...although in that "worst case" the string would actually keep shrinking, so we'd probably get closer to something like $O(p \log n)$ runtime... |
| 45 | + - Another test case to consider: what happens if pairs of parentheses are side by side-- e.g., `s = 'ab(cd)ef(gh)'`. Then, actually, this solution won't work, because the "cd" open parenthesis will get paired with the "gh" closed parenthesis. So we might need to either manually track this or do something clever... |
| 46 | +- I'm about out of time for tonight, so I'm just going to try quickly implementing the recursive solution and see if it works... |
4 | 47 |
|
5 | 48 | ## Refining the problem, round 2 thoughts
|
| 49 | +- Any special cases to consider? |
| 50 | +- Eh....it's just inefficient. I don't think it'll be "wrong" per se. I'm guessing it'll time out on one of the test cases though. |
6 | 51 |
|
7 | 52 | ## Attempted solution(s)
|
8 | 53 | ```python
|
9 |
| -class Solution: # paste your code here! |
10 |
| - ... |
| 54 | +class Solution: |
| 55 | + def reverseParentheses(self, s: str) -> str: |
| 56 | + def helper(s): |
| 57 | + result = '' |
| 58 | + i = 0 |
| 59 | + while i < len(s): |
| 60 | + if s[i] != '(': |
| 61 | + result += s[i] |
| 62 | + else: |
| 63 | + depth = 1 |
| 64 | + j = i + 1 |
| 65 | + while j < len(s): |
| 66 | + if s[j] == '(': |
| 67 | + depth += 1 |
| 68 | + elif s[j] == ')': |
| 69 | + depth -= 1 |
| 70 | + |
| 71 | + if depth == 0: |
| 72 | + result += helper(s[i + 1:j])[::-1] |
| 73 | + i = j |
| 74 | + break |
| 75 | + j += 1 |
| 76 | + i += 1 |
| 77 | + |
| 78 | + return result |
| 79 | + |
| 80 | + return helper(s) |
11 | 81 | ```
|
| 82 | +- The given test cases pass |
| 83 | +- I don't have time to think up more clever test cases, so I'll just submit... |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | +Huh, I guess it worked! |
| 88 | + |
0 commit comments