# [32. Longest Valid Parentheses](https://leetcode.com/problems/longest-valid-parentheses/)

# 思路

求有效括号匹配子串的最大长度。

## 思路一、栈

括号匹配类的问题往往都能用栈解决，此题也不例外。

从前往后遍历字符串，设下标为i
* 如果遇到左括号或者栈空，则将当前下标i压入栈；
* 如果遇到右括号且栈顶为左括号，则遇到了匹配的括号对，将栈顶弹出即可；

然后就可以计算以字符`s[i]`作为结尾的最大长度：
* 若栈不空，则长度为`i - stk.top()`；
* 否则，即栈空，说明前面的都能匹配，则长度为`i+1`。

时空复杂度均为O(n)

## 思路二、动归

由于此问题存在很多子问题，且子问题之前又有很多重复，所可以考虑用动归。

先来看状态定义:
```
dp[i]: 以字符s[i]结尾的最大匹配长度
```
状态转移方程就很简单了：
* 如果`s[i] = '('`，那么`dp[i] = 0`；
* 否则，我们需要看以字符`s[i-1]`为结尾的最大匹配子串的前面那个字符`s[i - dp[i-1] - 1]`是否是左括号：
    * 若是，说明匹配上了，所以`dp[i] = dp[i-1] + 2 + dp[i - dp[i-1] - 2]`；
    * 若不是，则没匹配上，`dp[i] = 0`。

时空复杂度均为O(n)

## 思路三

前面两种思路的空间复杂度均为O(n)，此题还有一种常数空间的思路，和前面栈的思想有点类似。

使用两个变量left和right分别用来记录到当前位置时左括号和右括号的出现次数，当遇到左括号时，left自增1；否则right自增1。

* 当`left == right`时，说明匹配成功，此时匹配长度为`2*left`；
* 一旦遇到`right > left`，说明当前位置不能匹配，则重新开始，即left和right均重置为0。

但是对于"(()"这种时，在遍历结束时左右子括号数都不相等，此时没法更新结果res，但其实正确答案是2，怎么处理这种情况呢？答案是再反向遍历一遍，采取类似的方法，稍有不同的是此时若`right < left`了，则重置0。

时间复杂度O(n)，空间复杂度O(1)

# C++

## 思路一
``` C++
class Solution {
public:
    int longestValidParentheses(string s) {
        stack<int>stk;
        int res = 0, start = 0;
        for(int i = 0; i < s.size(); i++){
            if(!stk.empty() && s[stk.top()] == '(' && s[i] == ')') stk.pop();
            else stk.push(i);
            // 栈顶为当前匹配子串的起始位置
            res = max(res, stk.empty() ? i + 1 : i - stk.top());
        }
        return res;
    }
};
```

## 思路二
``` C++
class Solution {
public:
    int longestValidParentheses(string s) {
        vector<int>dp(s.size(), 0);
        int res = 0;
        for(int i = 1; i < s.size(); i++){
            if(s[i] == '(') continue;
            int tmp = i - dp[i-1] - 1;
            if(tmp >= 0 && s[tmp] == '(') 
                dp[i] = dp[i-1] + 2 + (tmp >= 1 ? dp[tmp - 1] : 0);
            res = max(res, dp[i]);
        }
        return res;
    }
};
```

## 思路三
``` C++
class Solution {
public:
    int longestValidParentheses(string s) {
        int res = 0, left = 0, right = 0, n = s.size();
        for (int i = 0; i < n; i++) {
            if(s[i] == '(') left++; 
            else right++;
            if (left == right) res = max(res, 2 * right);
            else if (right > left) left = right = 0;
        }
        
        left = right = 0;
        for (int i = n - 1; i >= 0; i--) {
            if(s[i] == '(') left++;
            else right++;
            if (left == right) res = max(res, 2 * left);
            else if (left > right) left = right = 0;
        }
        return res;
    }
};
```
