考点:栈、动态规划
这个题的难点在于有效子串的分隔条件
参考官方题解的思路:
var longestValidParentheses = function(s) {
if(s.length === 0) return 0;
/**
* 核心思想:假如这个字符串中有效括号序列有多个,
* 它们之间可能是
* 1. 通过'('隔开(例如 '()(()()' ),
* 2. 也可能是通过')'隔开(例如 '())())()' )。
*
* 针对括号的题目很容易想到用栈,本题也不例外。
* 先设定左括号下标入栈,碰到右括号时出栈,
* 并记录右括号和栈顶元素下标的差值为最长长度,
* 当然遇到右括号时需要一些额外处理,后面会讲到。
*
* 入栈的左括号下标可以作为最长子序列的左端点,
* 由此分析一下情况 1:此时左括号分隔开了两个有效字符串,
* 当遍历第二个有效字符串的时候,栈中始终有未找到匹配右括号的左括号下标,
* 即下标 2,所以它自然地分隔了两个有效字符串。
*
* 再分析一下情况 2:此时右括号分隔开了两个有效字符串,
* 注意遇到右括号弹栈后可能会遇到栈空的情况,说明此下标的右括号是多余的,
* 因此最长长度必须重置。
* 我们可以在这种情况下把此下标入栈,就达到了分隔有效字符串的功能
* 同时为了避免遇到右括号时栈已经空了,预先向栈中压入一个元素,
* 因为下标是从 0 开始的,所以就用 -1
* 比如像这种情况:'())())()',
* 0、1 入栈出栈,此时栈中剩下 -1。然后遇到下标 2 的右括号,
* 弹栈后栈为空,于是下标 2 入栈。那么可能就会有疑惑:
* 后续遇到右括号还会弹栈,2 被弹出去了怎么办呢?
* 其实无所谓,因为如果后面的右括号都有匹配的左括号,那么 2 永远会在栈底,
* 起到一个分隔作用;如果遇到非法,即没有匹配的左括号的右括号(下标为 5)
* 那么栈底元素就会被更新为 5,此时意味着 2 与 5 之间是一个有效子串,
* 同时它不能和前面或者后面的有效子串合并,因为它的前后都有不合法的右括号。
*
* 如果右括号是合法的,可以进行一次最长有效子串的判定,更新最大值。
*
* 最后分析一下起始条件:栈中压入 -1。这个操作很关键,
* 首先它保证遇到右括号弹栈的时候栈不为空,
* 其次如果最长有效子串下标从 0 开始,它能够方便计算长度。
*/
const stack = [-1];
let max = 0;
for(let i = 0; i < s.length; i++) {
if(s[i] === '(') stack.push(i);
else {
stack.pop();
if(stack.length === 0) stack.push(i);
else max = Math.max(max, i - stack[stack.length - 1]);
}
}
return max;
};
当然本题主要还是考察动态规划,但是这个动态规划太难想了,有机会去看题解吧