删除最外层括号
LeetCode传送门1021. Remove Outermost Parentheses
题目
有效括号字符串为空 ""、"(" + A + ")" 或 A + B ,其中 A 和 B 都是有效的括号字符串,+ 代表字符串的连接。
例如,"","()","(())()" 和 "(()(()))" 都是有效的括号字符串。 如果有效字符串 s 非空,且不存在将其拆分为 s = A + B 的方法,我们称其为原语(primitive),其中 A 和 B 都是非空有效括号字符串。
给出一个非空有效字符串 s,考虑将其进行原语化分解,使得:s = P_1 + P_2 + ... + P_k,其中 P_i 是有效括号字符串原语。
对 s 进行原语化分解,删除分解中每个原语字符串的最外层括号,返回 s 。
A valid parentheses string is either empty "", "(" + A + ")", or A + B, where A and B are valid parentheses strings, and + represents string concatenation.
For example, "", "()", "(())()", and "(()(()))" are all valid parentheses strings. A valid parentheses string s is primitive if it is nonempty, and there does not exist a way to split it into s = A + B, with A and B nonempty valid parentheses strings.
Given a valid parentheses string s, consider its primitive decomposition: s = P1 + P2 + ... + Pk, where Pi are primitive valid parentheses strings.
Return s after removing the outermost parentheses of every primitive string in the primitive decomposition of s.
Example
Input: s = "(()())(())"
Output: "()()()"
Explanation:
The input string is "(()())(())", with primitive decomposition "(()())" + "(())".
After removing outer parentheses of each part, this is "()()" + "()" = "()()()".
Input: s = "(()())(())(()(()))"
Output: "()()()()(())"
Explanation:
The input string is "(()())(())(()(()))", with primitive decomposition "(()())" + "(())" + "(()(()))".
After removing outer parentheses of each part, this is "()()" + "()" + "()(())" = "()()()()(())".
Input: s = "()()"
Output: ""
Explanation:
The input string is "()()", with primitive decomposition "()" + "()".
After removing outer parentheses of each part, this is "" + "" = "".
Constraints:
- 1 <= s.length <= 10^5
- s[i] is either '(' or ')'.
- s is a valid parentheses string.
思考线
解题思路
// 循环s中的元素。设isDone = ture. counts = 0
// 如果 isDone = true, counts = 0, 表示为第一个字符。 isDone = false, counts +=1; temp = '';
// 遇到 '(' counts+=1; 遇到')' counts-=1, temp +=s[i] 若 counts = 0; 设置 isDone = true. 同时 s= s+temp; temp = '';
// 最后 return s`;
看到这道题我首先想到这又是一道关于stack的题目,题中的原语化分解其实可以分成对应的几个栈。但是如何实现代码我却一时没有头绪。
那我们先观察规律,看能不能不适用栈来解决呢?
首先我们观察某个原语化的部分,从字符串开始若连续( 和)的部分一样多则说明这是一个整体部分。
要解决问题我们肯定要对s进行循环。在循环前 我们设置变量counts = 0来表示某个原语化部分是否完成闭环。遇到( counts ++; 遇到) counts --;
我们再设置 temp = ''来保存我们需要放入 结果res中的值。
在遍历的过程中
- 如果
counts ===0则说明是个原语化的开始部分 执行temp = ''; counts++; - 如果
counts !==0;s[i] ==='(',说明是个增加部分,后面肯定有闭合括号要与之对应,且该值需要被记录下来。 - 如果
counts !==0; s[i] ===')',说明这个是闭合部分,我们要让``counts--且判断 此时counts的值是否为0. 若counts ===0则需要把 temp放入到 结果res中 若counts !==0则把s[i]`放入 temp中
有了以上思路那我们的代码就很好实现了。
/**
* @param {string} s
* @return {string}
*/
var removeOuterParentheses = function (s) {
let isDone = true;
let counts = 0;
let temp = '';
let res = '';
for (let i = 0; i < s.length; i++) {
if (counts === 0) {
counts += 1;
temp = ''
} else if (s[i] === '(') {
counts += 1;
temp += s[i];
} else if (s[i] === ')') {
counts -= 1;
if (counts === 0) {
res += temp;
temp = '';
} else {
temp += s[i]
}
}
}
return res;
};
那么我们能不能优化一下这段代码呢?其实是可以的。
其实我们只需要记录counts就可以。
如果s[i]==='(' 并且 counts >1,说明其满足放入结果的条件,或者如果s[i] === ')'并且counts > 0, 也说明其满足放入最后结果的条件。那么我们可以得到简化后的代码如下。
/**
* @param {string} s
* @return {string}
*/
var removeOuterParentheses = function (s) {
let counts = 0;
let res = '';
for (let i = 0; i < s.length; i++) {
if(s[i] === '(' && ++counts >1 || s[i] ===')' && --counts >0) res +=s[i];
}
return res;
};
那么我们最后再思考一下是否可以用stack的性质来解决问题呢?
经过最后的思考我发现,我们可以用栈来记录字符串的位置i, 每次遇到(把值推入,遇到)执行栈的推出。那么当栈为空的推出的stack.pop() +1就是我们要记录的当前原语的起始位置,而判断处的i-1就是我们当前原语的结束位置。
根据这个特性,我们可以得到以下代码:
/**
* @param {string} s
* @return {string}
*/
var removeOuterParentheses = function (s) {
const stack = [];
let res = ''
for(let i = 0; i < s.length; i++) {
if(s[i] === '(') stack.push(i);
if(s[i] ===')'){
const pre = stack.pop()
if(stack.length ===0) {
res += s.slice(pre+1, i);
}
}
}
return res;
};
这就是我对本题的解法,如果有疑问或者更好的解答方式,欢迎留言互动。