LeetCode系列记录我学习算法的过程。
持续创作,加速成长!这是我参与「掘金日新计划 6 月更文挑战」的第 28 天,点击查看活动详情
题目
给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例:
输入: s = "(()"
输出: 2
解释: 最长有效括号子串是 "()"
输入: s = ")()())"
输出: 4
解释: 最长有效括号子串是 "()()"
输入: s = ""
输出: 0
提示
0 <= s.length <= 3 * 104s[i]为'('或')'
思路
又是一道困难级的题目,既然是找最长有效的括号子串,那么左右括号的数量必须是一样的,且需左括号开头:
- 首先定义两个指针,一个记录开始遍历位置
left,一个进行遍历right - 然后定义两个变量
lr、rr存储左右括号数量,一个变量res存储最大的有效长度 - 外层遍历的结束条件是剩余字符个数不大于
res时 - 每次外层遍历都要重置
right值为left,重置lr、rr为0 - 内层遍历结束条件为
right到达边界或lr大于剩余字符数的一半(此时肯定不能凑齐有效字符串) - 内层遍历判断
right位置字符进行lr或rr自增 - 如果此时
rr大于lr,则结束内层循环进行下一次外层循环 - 如果
lr等于rr,则判断是否需要覆盖res的值 - 最后遍历结束返回
res的值
代码实现
/**
* @param {string} s
* @return {number}
*/
var longestValidParentheses = function(s) {
let left = 0, // 记录开始遍历位置
right = 0, // 遍历指针
len = s.length, // 字符串长度
res = 0, // 记录结果
lr = 0, // 左括号数量
rr = 0 // 右括号数量
// 外层循环,当剩余字符数不大于 res 时结束循环
while(len - left > res) {
// 每次循环重置 righ、lr、rr 的值
right = left ,lr = 0, rr = 0
// 内层循环,当左括号数大于剩余字符的一半或 right 到达边界时结束循环
while(lr <= (len - left) / 2 && right < len) {
// 当前遍历字符为 ( 时,lr自增
if (s[right] === '(') {
lr++
} else {
// 反之 rr 自增
rr++
}
// 如果左右括号数一样
if (lr === rr) {
// 更新 res 的值
res = Math.max(res, lr + rr)
}
// 如果右括号数大于左括号数,结束内层循环
if (rr > lr) {
break
}
// 进入下次内层循环
right++
}
// 进入下次外层循环
left++
}
// 返回结果
return res
};
优化
执行耗时太不理想了,可能是用的方法太笨了,暴力解题
const longestValidParentheses = (s) => {
let maxLen = 0;
const stack = [];
stack.push(-1);
for (let i = 0; i < s.length; i++) {
const c = s[i];
if (c == '(') { // 左括号的索引,入栈
stack.push(i);
} else { // 遍历到右括号
stack.pop(); // 栈顶的左括号被匹配,出栈
if (stack.length) { // 栈未空
const curMaxLen = i - stack[stack.length - 1]; // 计算有效连续长度
maxLen = Math.max(maxLen, curMaxLen); // 挑战最大值
} else { // 栈空了
stack.push(i); // 入栈充当参照
}
}
}
return maxLen;
};