括号匹配问题一般的处理方式有两种:①使用栈的push/pop模拟;②给括号赋予正负的分值(+1/-1),从左到右计算分数之和。一个合法的串中任意位置的分数不会小于0(左括号数大于等于右括号)。
题意:给定一个由“(”和")"组成的字符串,求最长合法子串的长度。
栈一般用来判定括号序列是否合法,这里使用第二种方法。从左到右扫描一遍,遇到分数小于0则跳过该位置,从下一个位置起继续向右扫描,遇到分数为0的位置时则更新结果,处理代码如下:
int l = 0, r = 0, sum = 0;
int ans1 = 0;
while(r < len){
sum += s[r] == '(' ? 1 : -1;
if(sum < 0){
l = r + 1;
sum = 0;
}
else if(sum == 0){
if(r - l + 1 > ans1)
ans1 = r - l + 1;
}
++r;
}
但我们仅仅只能排除掉不合法的位置,获得“局部最优解”,最后剩下的串中左括号的数目可能大于右括号(最容易想到的情况是:起点处左括号刚好冗余),所以最优解可能包含在剩下的串中未进行更新。这种情况怎么处理呢?换个方向想一想,从左到右扫描,左括号的数目大于等于右括号;反过来,从右到左扫描,右括号的数目大于等于左括号,否则序列非法。所以对于剩下的串,只需从右往左扫描一次,就可以找出包含在其中的合法序列。
设从左到右扫描时,最后的待定串为,则定有:中任意位置处的分数都大于0,否则该位置要么被跳过、要么被更新。设中最后一个')'的位置为,则对所有。则从右往左扫描时,这些都会被跳过,然后从开始进行计算,对其他位置同理。这是因为,从左到右中任意位置的左括号大于右括号数,而这在从右往左扫描时,是非法的,这些非法的位置会被跳过,而合法的位置则会被更新。所以最后能得到正确的结果。代码如下:
class Solution {
public:
int longestValidParentheses(string s) {
int len = s.size(), i = 0, j = len - 1;
int l = 0, r = 0, sum = 0;
int ans = 0;
while(r < len){
sum += s[r] == '(' ? 1 : -1;
if(sum < 0){
l = r + 1;
sum = 0;
}
else if(sum == 0){
if(r - l + 1 > ans)
ans = r - l + 1;
}
++r;
}
int bound = l;
r = len - 1, l = len - 1, sum = 0;
int tmp = 0;
while(l > bound){
sum += s[l] == '(' ? 1 : -1;
if(sum > 0){
r = l - 1;
sum = 0;
}
else if(sum == 0){
if(r - l + 1 > tmp)
tmp = r - l + 1;
}
--l;
}
return ans > tmp ? ans : tmp;
}
};