题目
给你一个由 '('、')' 和小写字母组成的字符串 s。
你需要从字符串中删除最少数目的 '(' 或者 ')' (可以删除任意位置的括号),使得剩下的「括号字符串」有效。
请返回任意一个合法字符串。
有效「括号字符串」应当符合以下 任意一条 要求:
空字符串或只包含小写字母的字符串 可以被写作 AB(A 连接 B)的字符串,其中 A 和 B 都是有效「括号字符串」 可以被写作 (A) 的字符串,其中 A 是一个有效的「括号字符串」
示例
输入:s = "lee(t(c)o)de)"
输出:"lee(t(c)o)de"
解释:"lee(t(co)de)" , "lee(t(c)ode)" 也是一个可行答案。
当时我做题时想的思路
忽略字母:
假设字符串有: s = "(())(()"
观察
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| ( | ( | ) | ) | ( | ) | ) |
明显可以看出第3位,多了一个"("右括号;
我第一次枚举记录无效的括号下标,再二次枚举的时候忽略这些无效的括号下标不就可以得到答案了?
怎么讲这个位置记录下来呢?
首先想到有效括号时成对出现的,只有右括号碰到左括号才能组成有效括号;思路来了;
用数组list将无效左括号下标保存,用rightList数组保存无效的右括号下标;保存规则如下:
- 当遇到左括号【"("】;将下标放入数组,因为如果没有右括号,当前这个做括号是无效的;
- 当遇到右括号,分类讨论:
- 如果list数组为空,也就是说之前没有(;将当前坐标放在rightList
- 如果list数组不为空,将list最后一位移除,因为当前右括号与list数组中左括号组成有效括号
- 枚举完成,得到list和rightList为无效左右括号下标;
- 在枚举一次,忽略list和rightList数组中下标的字符串;即可得到答案
代码
var minRemoveToMakeValid = function(s) {
let len = s.length;
let list = [];
let leftlist = [];
let str = ''
for(let i = 0 ; i < len ; i++){
if(s[i] === '('){
list.push(i)
}else if(s[i] === ')'){
if(list.length === 0){
leftlist.push(i)
}else{
list.pop();
}
}
}
const limit = leftlist.concat(list)
for(let i = 0 ; i < len ; i++){
if(!limit.includes(i))str+=s[i]
}
return str
};
优化后
按照自己的思路虽然也能AC,但是不但使用了额外空间,还使用了两个数组;优化一下;
怎么优化?
考虑优化点,枚举是没办法优化了,只能看看数组能不能不用了。
能不用数组记录无效括号数据吗?
在观察 s = "(())(()"
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| ( | ( | ) | ) | ( | ) | ) |
第一遍枚举的时候其实可以删除第4位上的( 左括号;
再观察 s = "))))(()"
| 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|
| ) | ) | ( | ( | ( | ) | ) |
第一遍枚举可以将所有无效的( 左括号删除,但是不能确定无效的右括号
这思路不就来了吗。
第一遍枚举从左到右删除无效左括号,
第二遍枚举从右到左删除无效右括号不就行了。
举个例子
假设 s = "))))(()" num = 0; num用于记录左括号数量,
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 |
|---|---|---|---|---|---|---|---|
| 字符串 | ) | ) | ( | ( | ( | ) | ) |
| num | 0 | 0 | 1 | 2 | 3 | 2 | 1 |
- 当num为0时遇到右括号,忽略
- 当num大于0;遇到右括号,将右括号保存到新字符串中,num-1; 枚举结束后得到新字符串
| 下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 字符串 | ( | ( | ( | ) | ) |
| num | 1 | 2 | 3 | 2 | 1 |
观察新字符串,此时num=1;
新字符串左括号正好多出来了1个;
新字符串从右到左枚举,遇到的num个左括号,忽略;
| 下标 | 0 | 1 | 2 | 3 | 4 |
|---|---|---|---|---|---|
| 字符串 | ( | ( | ( | ) | ) |
然后得到:
| 下标 | 0 | 1 | 2 | 3 |
|---|---|---|---|---|
| 字符串 | ( | ( | ) | ) |
哈哈,搞定
代码
var minRemoveToMakeValid = function(s) {
let len = s.length;
let str = ''
let num = 0;
for(let i = 0 ; i < len ; i++){
if(s[i] === '('){
num++;
str+=s[i]
}else if(s[i] === ')'){
if(num === 0){
}else{
str+=s[i];
num--;
}
}else{
str+=s[i];
}
}
if(num === 0) return str;
let result = ''
for(let i = str.length-1 ;i>=0 ; i--){
if(str[i] === '(' && num > 0){
num--;
continue
}
result = str[i] + result
}
return result
};