最长合法字串问题
算法概论第八周
文章目录
32. Longest Valid Parentheses — 题目链接
题目描述
Given a string containing just the characters '(' and ')', find the length of the longest valid (well-formed) parentheses substring.
Example 1:
Input: "(()"
Output: 2
Explanation: The longest valid parentheses substring is "()"
Example 2:
Input: ")()())"
Output: 4
Explanation: The longest valid parentheses substring is "()()"
思路分析
方法一:暴力枚举
- 最朴素的方法就是把所有可能的长度都试一遍
- 每个长度都要从左至右找一遍字串验证是否合法
- 判断一个字串是否合法只需要维护一个栈,遇到(入栈,遇到)出栈,如果栈为空仍然需要出栈,则返回false
- 复杂度:
- 时间:将所有长度试一遍为 O ( n ) O(n) O(n),每个长度的所有字符串为 O ( n ) O(n) O(n),栈判断为 O ( n ) O(n) O(n),一共为 O ( n 3 ) O(n^3) O(n3)
- 空间:维护一个栈为 O ( n ) O(n) O(n)
方法二:动态规划
- 对于动态规划,我们要找出它的最优子问题
- 设
dp[i]
为以s[i]
结尾的最长合法字串的长度 - 那么
dp[i]
的递推式只有两种情况s[i] == ')' && s[i-1] == '('
,则dp[i] = dp[i-2] + 2
s[i] == ')' && s[i-1] == ')' && s[i-dp[i-1]-1] == '('
,则dp[i] = dp[i-1] +dp[i-dp[i-1]-2] + 2
- 当然要注意检查数组是否越界
- 从左至右遍历,每一次用
dp[i]
更新最大值
- 复杂度:
- 时间:遍历一遍即可得出所有值, O ( n ) O(n) O(n)
- 空间:用
dp
数组储存, O ( n ) O(n) O(n)
方法三:计算左右括号
- 从左至右和从右至左分别遍历
- 如果遇到左括号,
left++
,右括号,right++
,如果right > left
,清零 - 每一次迭代,如果
left == right
,更新最大值 - 需要两个方向的遍历原因是为了计算类似
(((()
的情况 - 复杂度:
- 时间: O ( n ) O(n) O(n)
- 空间:仅
left
、right
,为 O ( 1 ) O(1) O(1)
代码实现
方法一:暴力枚举
TLE
class Solution {
public:
int longestValidParentheses(string s) {
int len = s.size();
int res = 0;
for(int i = 2; i <= len; i++){
for(int j = 0; j <= len - i; j++){
if(verifyString(s.substr(j, i))){
res = i;
break;
}
}
}
return res;
}
bool verifyString(string a){
int len = a.size();
stack <int> s;
for(int i = 0; i < len; i++){
if(a[i] == '(')
s.push(1);
else{
if(!s.empty())
s.pop();
else
return false;
}
}
return s.empty();
}
};
方法二:动态规划
AC 超过61.92%的cpp程序
class Solution {
public:
int longestValidParentheses(string s) {
int len = s.size();
if(len < 2)
return 0;
int* dp = new int[len];
int res = 0;
for(int i = 0; i < len; i++){
dp[i] = 0;
}
for(int i = 1; i < len; i++){
if(s[i] == ')' && s[i-1] == '('){
dp[i] = ((i-2 >= 0) ?dp[i-2] : 0) + 2;
}
else if(s[i] == ')' && s[i-1] == ')'){
if(i - dp[i-1] - 1 >= 0 && s[i-dp[i-1]-1] == '('){
dp[i] = dp[i-1] + ((i-dp[i-1]-2 >= 0) ? dp[i-dp[i-1]-2]:0) + 2;
}
}
res = max(res, dp[i]);
}
return res;
}
};
方法三:计算左右括号
AC 超过99.93%的cpp程序
class Solution {
public:
int longestValidParentheses(string s) {
int left = 0, right = 0, maxlength = 0;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = max(maxlength, 2 * right);
} else if (right >= left) {
left = right = 0;
}
}
left = right = 0;
for (int i = s.length() - 1; i >= 0; i--) {
if (s[i] == '(') {
left++;
} else {
right++;
}
if (left == right) {
maxlength = max(maxlength, 2 * left);
} else if (left >= right) {
left = right = 0;
}
}
return maxlength;
}
};