题目 32.最长有效括号
描述
给你一个只包含 ‘(’ 和 ‘)’ 的字符串,找出最长有效(格式正确且连续)括号子串的长度。
示例
题解
方法一:计数器法(贪心)
- 用 left right 两个计数器分别计数’(’ 和 ')'的个数
- 如果left == right 就更新 maxlength 的值
- 如果left < right 直接将 left right 置为0
- 因为无法处理left > right 的情况 比如 “(()”
- 可采用从右到左再遍历一下,更新maxlenth 即可
class Solution {
public:
int longestValidParentheses(string s) {
// 方法一 (计数器法)
// 用left right 两个计数器分别计数'(' 和 ')'的个数
// 如果left == right 就更新 maxlength 的值
// 如果left < right 直接将 left right 置为0
// 因为无法处理left > right 的情况 比如 "(()"
// 可采用从右到左再遍历一下,更新maxlenth 即可
// 遍历左边
int left = 0,right = 0,maxlength = 0;
for(int i = 0; i < s.size(); i ++)
{
if(s[i] == '(') left ++;
if(s[i] == ')') right ++;
if(left == right){
maxlength = max(maxlength , 2 * right);
}
if(left < right) {
left = right = 0;
}
}
// 遍历右边
left = 0,right = 0;
for(int i = s.size() - 1; i >= 0 ; i --)
{
if(s[i] == '(') left ++;
if(s[i] == ')') right ++;
if(left == right){
maxlength = max(maxlength , 2 * right);
}
if(left > right) {
left = right = 0;
}
}
return maxlength;
}
};
方法二:动态规划
- dp[i] = 从0到i 的最长子串长度maxlenth
- 有效的子串一定以 ‘)’ 结尾
- 这样只有两种情况会计算(以 ‘)’ 结尾)
- 1.“…()”
- s[i] == ‘)’ && s[i - 1] == ‘(’
- dp[i] = dp[i - 2] + 2
- 2.“…))”
- s[i] == ‘)’ && s[i - 1] == ‘)’ && s[i - dp[i - 1] - 1] == ‘(’
- dp[i] = dp[i - 1] + 2 + dp[i - dp[i - 1] - 2] ;
- 这里解释一下为什么有这个 dp[i - dp[i - 1] - 2] 因为你在i - dp[i - 1] - 1判断了i位置可不可加入有效子字符串,如果可以就要判断i - dp[i - 1] - 1位置之前是否也是有效子字符串这样前后就可以加上(没有就是0,判断一下要大于2)例如"()(())" 这种的就是两个拼在一起.
class Solution {
public:
int longestValidParentheses(string s) {
// 方法二 (动态规划)
// dp[i] = 从0到i 的最长子串长度maxlenth
// 这样只有两种情况会计算(以')' 结尾)
// 1.".....()"
// s[i] == ')' && s[i - 1] == '('
// dp[i] = d[i - 2] + 2
// 2.".....))"
// s[i] == ')' && s[i - 1] == ')' && s[i - dp[i - 1] - 1] == '('
// dp[i] = d[i - 1] + 2 + dp[i - dp[i - 1] - 2] ;
int maxlength = 0, n = s.size();
vector<int> dp(n, 0);
for(int i = 1; i < s.size(); i ++)
{
if(s[i] == ')' && s[i - 1] == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
}
if(s[i] == ')' && s[i - 1] == ')' && i - dp[i - 1] > 0 && s[i - dp[i - 1] - 1] == '('){
dp[i] = dp[i - 1] + 2 + ((i - dp[i - 1] >= 2) ? dp[i - dp[i - 1] - 2] : 0);
}
maxlength = max(maxlength, dp[i]);
}
return maxlength;
}
};
方法三 :栈
撇开方法一提及的动态规划方法,相信大多数人对于这题的第一直觉是找到每个可能的子串后判断它的有效性,但这样的时间复杂度会达到 O(n3),无法通过所有测试用例。但是通过栈,我们可以在遍历给定字符串的过程中去判断到目前为止扫描的子串的有效性,同时能得到最长有效括号的长度。
具体做法是我们始终保持栈底元素为当前已经遍历过的元素中「最后一个没有被匹配的右括号的下标」,这样的做法主要是考虑了边界条件的处理,栈里其他元素维护左括号的下标:
- 保持栈底是(最后一个未被匹配的右括号)
- 栈内其他元素维护左括号的下标
- 对于遇到的每个’(',将其下标放入栈中
- 对于每个’)',我们先弹出栈顶表示匹配了当前右括号
- 如果栈为空,说明当前右括号 是最后一个未被匹配的右括号
- 如果栈不为空,当前右括号的下标减去栈顶元素即为(以该右括号结尾的最长有效括号的长度)
- 开始先放一个-1元素解决边界问题(因为你是通过减去栈顶元素得到长度的,如果最后没有这个-1的话就减空了)比如"()" 1 - (-1) = 2;
class Solution {
public:
int longestValidParentheses(string s) {
// 方法三(栈)
// 保持栈底是(最后一个未被匹配的右括号)
// 栈内其他元素维护左括号的下标
// 对于遇到的每个'(',将其下标放入栈中
// 对于每个')',我们先弹出栈顶表示匹配了当前右括号
// 如果栈为空,说明当前右括号 是最后一个未被匹配的右括号
// 如果栈不为空,当前右括号的下标减去栈顶元素即为(以该右括号结尾的最长有效括号的长度)
// 开始先放一个-1元素保持边界问题
int maxlenth = 0;
stack<int> stk;
stk.push(-1);
for(int i = 0; i < s.size(); i ++)
{
if(s[i] == '('){
stk.push(i);
}else {
stk.pop();
if(stk.empty()) {
stk.push(i);
}else {
maxlenth = max(maxlenth, i - stk.top());
}
}
}
return maxlenth;
}
};