Problem overview
The function reverseParentheses receives a string s of lowercase letters and balanced parentheses. It must reverse the text inside every pair of matching parentheses, working from the innermost pair outwards, and return the result with no parentheses left.
For "(abcd)" the answer is "dcba". For "(u(love)i)", love is reversed first, giving (uevoli), and then the whole content is reversed, giving "iloveu". For "(yme(oc(da))gla)" the steps are da to ad, then ocad to daco, and finally the whole text, which gives "algocademy". The string has at most 2,000 characters.
Approach: one buffer per nesting level
Parentheses nest, and the innermost pair is handled first. That is the signature of a stack: the most recently opened level is the first one to close.
Keep a stack of strings, where each string is the text collected so far at one nesting level. The bottom string holds the text outside every parenthesis. Then read the characters one by one:
- On
(, push a new empty string. A new level begins. - On
), pop the top string, reverse it, and append it to the string beneath, which is the level that contained this pair. - On any other character, append it to the top string.
Reversing a level at the moment it closes is exactly the "innermost first" order. When a level is appended to its parent, the parent may itself be reversed later, which flips the already reversed text again. That is why "(u(love)i)" comes out right.
Trace "(ab(cd)e)". The stack starts as [""]. After ( it is ["", ""], and after ab it is ["", "ab"]. Another ( gives ["", "ab", ""], and cd makes the top "cd". The ) pops "cd", reverses it to "dc" and appends it to "ab": ["", "abdc"]. Then e gives ["", "abdce"]. The last ) pops "abdce", reverses it to "ecdba" and appends it to the bottom string. The answer is "ecdba".
Step by step
- Create the stack. Declare a
vector<string>namedresholding one empty string. The first string collects characters that appear before any opening parenthesis, and later it also receives finished levels. - Loop over the characters of
swith a range-basedforand acharvariablec. - Handle
(. Usepush_back("")to open a new level. - Handle
). This is theelse ifbranch. - Take the finished level. Copy
res.back()into astringnamedtop, and only then callpop_back().back()returns a reference to the last element, andpop_back()destroys that element, so a reference would be left dangling. - Reverse it with
reverse(top.begin(), top.end()). The call changes the string in place, so no assignment is needed. - Append it to the level below. After the pop,
res.back()is the enclosing level. The+=operator changes the string stored in the vector. - Handle everything else. In the
elsebranch, appendctores.back(). - Join and return. For balanced input exactly one string remains, but
accumulatefromstring()concatenates whatever is left.
The solution in C++
class Solution {
public:
string reverseParentheses(string s) {
vector<string> res = {""};
for (char c : s) {
if (c == '(') {
res.push_back("");
} else if (c == ')') {
string top = res.back();
res.pop_back();
reverse(top.begin(), top.end());
res.back() += top;
} else {
res.back() += c;
}
}
return accumulate(res.begin(), res.end(), string());
}
};
The C++ details worth knowing:
- A vector works as a stack.
push_back,backandpop_backare the push, peek and pop operations.std::stackwould also work, but a vector allows the final loop over every entry. - Brace initialization.
vector<string> res = {""}creates a vector with one empty string. Writingvector<string> res(1)would do the same. - Copy before pop.
string top = res.back();makes a copy, sotopstays valid afterpop_back(). Writingstring& top = res.back();and then popping would leavetopreferring to a destroyed string, and using it is undefined behaviour. accumulatelives in<numeric>. Its third argument gives the starting value and its type.string()makes the accumulator astring. Passing""instead makes the accumulator aconst char*, and the code doesn't compile, because astringcan't be assigned to one.reverselives in<algorithm>. It reverses the elements of the range it is given, here the characters oftop.- Character against string.
'('is achar, and""is a string. Comparingc == '('uses single quotes, andres.back() += cappends one character.
Complexity
Let n be the length of s.
Time: O(n²) in the worst case. Each character is read once, but a closing parenthesis copies, reverses and appends a whole level. For input such as (a(a(a(a)))), the text carried up to the parent grows by one character at each level, and every closing parenthesis reverses and copies it again, so the total work grows quadratically. With n at most 2,000 that is well within limits. For typical inputs it is close to linear.
Space: O(n). The stack holds at most the characters of the string, plus the temporary top.
Edge cases and common mistakes
- No parentheses.
"abc"stays"abc", since everything lands in the first string. - Text around a pair.
"a(bc)d"gives"acbd". Only the part inside the pair is reversed. - Several pairs side by side.
"(a)(bc)"gives"acb". Each pair is reversed on its own and appended in order. - Nested pairs that cancel.
"((ab))"gives"ab", since the text is reversed twice. - An empty pair.
"()"gives an empty string. - Reversing the wrong buffer. Reversing
res.back()after the pop would reverse the parent level instead of the closed one. - Appending
topbefore the pop. The text would go to the level that is about to disappear, and the result would be lost.
Testing
"(abcd)"gives"dcba"."(u(love)i)"gives"iloveu"."(yme(oc(da))gla)"gives"algocademy"."a(bc)d"gives"acbd"."(a)(bc)"gives"acb".