前言
今天看面试题,就看到了这道面试。只不过面试官是从最简单的判断一个字符串,是不是有效括号。
然后再提出"求有效括号的最长长度",最后我就想去直接入手这道题。
题目描述
给你一个只包含 '(' 和 ')' 的字符串,找出最长有效(格式正确且连续)括号子串的长度。 示例 1:
输入:s = "(()"
输出:2
解释:最长有效括号子串是 "()"
示例 2:
输入:s = ")()())"
输出:4
解释:最长有效括号子串是 "()()"
示例 3:
输入:s = ""
输出:0
解题思路
解题思路一:常规
对于这种括号匹配问题,一般都是使用栈。 我们先找到所有可以匹配的索引号,然后找出最长连续数列!
具体步骤:
-
当碰到了"(",就将"("压入stack栈中,tmPmax++。
-
当碰到了")",就将前一个")"出栈,tmPmax++。
-
计算max(最长有效长度)
-
在"("进栈时,判断stack.length 是否等于 0, 等于0就要进行计算了(
max = max>tmpMax?max:tmpMax
)。由于出现")()()(()",在碰到"("(index==4),就进行新的有效括号判断。 -
在碰到")"时,判断stack.length 是否等于 0,等于0就要进行计算了(
max = max>tmpMax?max:tmpMax
)。由于碰到")",后面一定不是有效括号 -
在一次循环后
stack.length == 0
,进行计算。
AC代码
var longestValidParentheses = function(s) {
var max = 0; // 最长长度
if(s.length == 0 || s.length == 1) return max;
var stack = [].fill(')'); // 使用了栈来实现匹配 空间复杂度O(n)
for(var i =0;i<s.length; i++) {
var tmpMax = 0;
var temp = 0;
for(var j = i;j<s.length;j++){
if(s[j] == '('){
if(stack.length == 0) {
max = max>tmpMax?max:tmpMax
temp = 0
}
stack.push('(')
tmpMax++;
temp++;
}else if(s[j] == ')') {
if(stack.length < 1){
// 栈空的 当前位置括号有效匹配 结束了
max = max>tmpMax?max:tmpMax
break;
}else{
stack.pop();
tmpMax++;
temp++
}
}
}
if(stack.length == 0) {
max = max>tmpMax?max:tmpMax
}
stack = [];
}
return max;
};
解题思路二:减少时间复杂度
- 对于遇到的每个 "(" ,我们将它的下标放入栈
- 对于遇到的每个 ")" ,我们先弹出栈顶元素表示匹配了当前右括号:
- 如果栈为空,说明当前的右括号为没有被匹配的右括号, 我们将其下标放入栈中来更新我们之前提到的「最后一个没有被匹配的右括号的下标」
- 如果栈不为空,当前右括号的下标减去栈顶元素 即为「以该右括号为结尾的最长有效括号的长度」
AC代码
function longestValidParentheses(s) {
// 时间复杂为O(n)
var max = 0;
if(s.length == 0 || s.length == 1) return max;
var stack = [-1]; // 栈来服务于有效括号匹配 刚开始匹配的
for(var i = 0; i< s.length;i++){ // 下标法
if(s[i] == '('){// 左括号
stack.push(i);
}else {
stack.pop(); // 右括号 出栈
if(stack.length < 1) {
stack.push(i) //
} else {
max = Math.max(max, i - stack[stack.length -1])
}
}
}
return max;
}
解题思路二:减少空间复杂度
我们利用两个计数器 left 和 right 。
-
首先,我们从左到右遍历字符串,对于遇到的每个 "(",我们增加 left计数器, 对于遇到的每个 ")" ,我们增加 right 计数器。
-
每当 left 计数器与 right 计数器相等时,我们计算当前有效字符串的长度,并且记录目前为止找到的最长子字符串。
-
当 right 计数器比 left 计数器大时,我们将 left 和 right 计数器同时变回 0。
-
该贪心的缺点:
- 这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即
(()
,这种时候最长有效括号是求不出来的。
- 这样会漏掉一种情况,就是遍历的时候左括号的数量始终大于右括号的数量,即
-
解决: 只需要从右往左遍历用类似的方法计算即可(判断条件有所改变)
AC代码
let left = 0, right = 0, maxlength = 0;
for (let i = 0; i < s.length; i++) {
if (s[i] == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * right);
} else if (right > left) {
left = right = 0;
}
}
left = right = 0;
for (let i = s.length - 1; i >= 0; i--) {
if (s.charAt(i) == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = Math.max(maxlength, 2 * left);
} else if (left > right) {
left = right = 0;
}
}
return maxlength;
总结
一个代码的空间复杂度和时间复杂度,是面试官算法考试的重点。只能说优秀的代码还在后面呢!
本文正在参与「掘金 2021 春招闯关活动」, 点击查看 活动详情