题目
标题和出处
标题:删除最外层的括号
难度
3 级
题目描述
要求
有效括号字符串为空 "" \texttt{""} ""、 "(" + A + ")" \texttt{"(" + A + ")"} "(" + A + ")" 或 A + B \texttt{A + B} A + B ,其中 A \texttt{A} A 和 B \texttt{B} B 都是有效的括号字符串, + \texttt{+} + 代表字符串的连接。
- 例如, "" \texttt{""} "", "()" \texttt{"()"} "()", "(())()" \texttt{"(())()"} "(())()" 和 "(()(()))" \texttt{"(()(()))"} "(()(()))" 都是有效的括号字符串。
如果有效字符串 s \texttt{s} s 非空,且不存在将其拆分为 s = A + B \texttt{s = A + B} s = A + B 的方法,我们称其为原语,其中 A \texttt{A} A 和 B \texttt{B} B 都是非空有效括号字符串。
给出一个非空有效字符串 s \texttt{s} s,考虑将其进行原语化分解,使得: s = P 1 + P 2 + … + P k \texttt{s = P}_\texttt{1}\texttt{ + P}_\texttt{2}\texttt{ + \ldots + P}_\texttt{k} s = P1 + P2 + …+ Pk,其中 P i \texttt{P}_\texttt{i} Pi 是有效括号字符串原语。
对 s \texttt{s} s 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 s \texttt{s} s。
示例
示例 1:
输入:
s
=
"(()())(())"
\texttt{s = "(()())(())"}
s = "(()())(())"
输出:
"()()()"
\texttt{"()()()"}
"()()()"
解释:输入字符串为
"(()())(())"
\texttt{"(()())(())"}
"(()())(())",原语化分解得到
"(()())"
+
"(())"
\texttt{"(()())" + "(())"}
"(()())" + "(())",删除每个部分中的最外层括号后得到
"()()"
+
"()"
=
"()()()"
\texttt{"()()" + "()" = "()()()"}
"()()" + "()" = "()()()"。
示例 2:
输入:
s
=
"(()())(())(()(()))"
\texttt{s = "(()())(())(()(()))"}
s = "(()())(())(()(()))"
输出:
"()()()()(())"
\texttt{"()()()()(())"}
"()()()()(())"
解释:输入字符串为
"(()())(())(()(()))"
\texttt{"(()())(())(()(()))"}
"(()())(())(()(()))",原语化分解得到
"(()())"
+
"(())"
+
"(()(()))"
\texttt{"(()())" + "(())" + "(()(()))"}
"(()())" + "(())" + "(()(()))",删除每个部分中的最外层括号后得到
"()()"
+
"()"
+
"()(())"
=
"()()()()(())"
\texttt{"()()" + "()" + "()(())" = "()()()()(())"}
"()()" + "()" + "()(())" = "()()()()(())"。
示例 3:
输入:
s
=
"()()"
\texttt{s = "()()"}
s = "()()"
输出:
""
\texttt{""}
""
解释:输入字符串为
"()()"
\texttt{"()()"}
"()()",原语化分解得到
"()"
+
"()"
\texttt{"()" + "()"}
"()" + "()",删除每个部分中的最外层括号后得到
""
+
""
=
""
\texttt{"" + "" = ""}
"" + "" = ""。
数据范围
- 1 ≤ s.length ≤ 10 5 \texttt{1} \le \texttt{s.length} \le \texttt{10}^\texttt{5} 1≤s.length≤105
- s[i] \texttt{s[i]} s[i] 为 ‘(’ \texttt{`('} ‘(’ 或 ‘)’ \texttt{`)'} ‘)’
- s \texttt{s} s 是一个有效括号字符串
解法一
思路和算法
根据题目描述,给定的字符串 s s s 是有效括号字符串。在有效括号字符串中,左括号和右括号的数量相同且形成配对,因此最外层的括号也是左括号和右括号配对。
可以使用栈存储左括号,根据栈是否为空判断括号是否为最外层的括号。
从左到右遍历字符串 s s s,遇到左括号则将左括号入栈,遇到右括号则将栈顶的左括号出栈,表示当前的右括号和栈顶的左括号配对。对于左括号,如果入栈之前栈为空,则该左括号为最外层的括号;对于右括号,如果将栈顶的左括号出栈之后栈为空,则该右括号为最外层的括号。
根据上述规则,只需要遍历字符串一次,对于每个字符可以在 O ( 1 ) O(1) O(1) 的时间内判断该字符是否为最外层的括号。将不是最外层的括号的字符拼接到结果字符串,最终得到的结果字符串即为删除最外层的括号之后的结果。
实现方面,对于字符 c c c,依次执行以下操作,等价于上述规则:
-
判断 c c c 是否是右括号,如果是右括号,则将栈顶的左括号出栈;
-
如果栈不为空,则 c c c 不是最外层的括号,将 c c c 拼接到结果字符串;
-
判断 c c c 是否是左括号,如果是左括号,则将 c c c 入栈。
代码
class Solution {
public String removeOuterParentheses(String s) {
StringBuffer sb = new StringBuffer();
Deque<Character> stack = new ArrayDeque<Character>();
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == ')') {
stack.pop();
}
if (!stack.isEmpty()) {
sb.append(c);
}
if (c == '(') {
stack.push(c);
}
}
return sb.toString();
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要遍历字符串 s s s 一次,每次入栈、出栈和拼接字符串的时间都是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。空间复杂度主要取决于栈空间和额外创建的 StringBuffer \texttt{StringBuffer} StringBuffer 或 StringBuilder \texttt{StringBuilder} StringBuilder 类型的对象,空间复杂度是 O ( n ) O(n) O(n)。
解法二
思路和算法
也可以不用栈,而是使用计数的方式删除最外层的括号。
具体做法是,使用 level \textit{level} level 记录当前括号所在层数,初始时 level = 0 \textit{level} = 0 level=0。遍历字符串 s s s,遇到左括号则将 level \textit{level} level 加 1 1 1,遇到右括号则将 level \textit{level} level 减 1 1 1。对于左括号,如果将 level \textit{level} level 的值加 1 1 1 之前栈为空,则该左括号为最外层的括号;对于右括号,如果将 level \textit{level} level 的值减 1 1 1 之后栈为空,则该右括号为最外层的括号。
根据上述规则,只需要遍历字符串一次,对于每个字符可以在 O ( 1 ) O(1) O(1) 的时间内判断该字符是否为最外层的括号。将不是最外层的括号的字符拼接到结果字符串,最终得到的结果字符串即为删除最外层的括号之后的结果。
实现方面,对于字符 c c c,依次执行以下操作,等价于上述规则:
-
判断 c c c 是否是右括号,如果是右括号,则将 level \textit{level} level 的值减 1 1 1;
-
如果 level > 0 \textit{level} > 0 level>0,则 c c c 不是最外层的括号,将 c c c 拼接到结果字符串;
-
判断 c c c 是否是左括号,如果是左括号,则将 level \textit{level} level 的值加 1 1 1。
代码
class Solution {
public String removeOuterParentheses(String s) {
StringBuffer sb = new StringBuffer();
int level = 0;
int length = s.length();
for (int i = 0; i < length; i++) {
char c = s.charAt(i);
if (c == ')') {
level--;
}
if (level > 0) {
sb.append(c);
}
if (c == '(') {
level++;
}
}
return sb.toString();
}
}
复杂度分析
-
时间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要遍历字符串 s s s 一次,每次更新计数和拼接字符串的时间都是 O ( 1 ) O(1) O(1)。
-
空间复杂度: O ( n ) O(n) O(n),其中 n n n 是字符串 s s s 的长度。需要额外创建一个长度为 n n n 的 StringBuffer \texttt{StringBuffer} StringBuffer 或 StringBuilder \texttt{StringBuilder} StringBuilder 类型的对象。由于 Java 中的 String \texttt{String} String 类型的对象不可变,因此空间复杂度至少为 O ( n ) O(n) O(n)。