题目
Given a string containing just the characters '('
and ')'
, find the length of the longest valid (well-formed) parentheses substring.
For "(()"
, the longest valid parentheses substring is "()"
, which has length = 2.
Another example is ")()())"
, where the longest valid parentheses substring is
"()()"
, which has length = 4.
括号匹配问题。
对某个子串,以“(”为1,“)”为-1,从头至尾叠加,如果和一直为正,最后等于0,则是合法的。这样不难想到一个n^2的暴力方法,当然,会超时。
O(n)的方法:
从头开始扫,保留头位置。暂存最大的合法串长度。
操作:
1、如果和等于0,则头到当前位置是合法的。
2、如果和小于0,则现在的头位置开始到尾必然不会有更长的合法串了。更改头位置,重置和。
3、如果和大于0,可能在之后出现更长的合法子串,继续。
那么一次扫描结束后,
1、如果和小于等于0,显然已经得到了最长的合法长度。
2、如果和大于0,则当时的头到尾之间可能存在合法串,且长度有可能大于已经获得最长长度。
截取尾部和为正的子串。
假设子串p是尾部和为正的子串的最长合法子串。
那么显然,它的开始位置必然不是0位置。先假设其后面有括号串。
那么有:
1、p前面串的总和必然大于0,(如果等有等于0,则前面的是合法串,和p合并后可以产生更长的合法串)
2、p前面的串的最后一个括号必然是"(",(否则由前1条观察可知:")"可以和之前的某个"("组成合法串,与p合并成更长和合法串)
3、p后面的串综合必然大于等于0,(否则由前2条观察知:p后面的串可以和前面的串的某部分匹配,从而形成更长的合法串)
4、p后面的串的第一个必然是"("(否则由前3条观察可知:")"可以和前串的最后一个"("组成合法串,与p合并成更长和合法串)
因此可以从后向前扫描,以"("为-1,以")"为1,重复之前的操作,即可获得最长的合法串。
代码:
直接反转字符串反向扫描
class Solution {
public:
int lvp(string s,int flag) //括号串,正向反向标志
{
int ans=0;
int len=s.size();
if(len<2)
return ans;
int sum=0;
int i=0,j=0;
while(j<len)
{
if(s[j]=='(')
sum+=flag;
else
sum-=flag;
if(sum==0)
{
if(j-i+1>ans)
ans=j-i+1;
}
else if(sum<0)
{
i=j+1;
j=i;
sum=0;
continue;
}
j++;
}
return ans;
}
int longestValidParentheses(string s) {
int l1=lvp(s,1); //正向扫描
reverse(s.begin(),s.end()); //反转
int l2=lvp(s,-1); //反向扫描
return max(l1,l2);
}
};
只计算尾部,速度略快:
class Solution {
public:
int longestValidParentheses(string s) {
int ans=0;
int len=s.size();
if(len<2)
return ans;
int sum=0;
int i=0,j=0;
while(j<len) //正向扫描
{
if(s[j]=='(')
sum++;
else
{
sum--;
if(sum==0)
{
if(j-i+1>ans)
ans=j-i+1;
}
else if(sum<0)
{
i=j+1;
j=i;
sum=0;
continue;
}
}
j++;
}
if(j>=len&&j-i>ans) //反向扫描尾部
{
sum=0;
j=len-1;
len=i;
i=j;
while(j>len)
{
if(s[j]==')')
sum++;
else
{
sum--;
if(sum==0)
{
if(i-j+1>ans)
ans=i-j+1;
}
else if(sum<0)
{
i=j-1;
j=i;
sum=0;
continue;
}
}
j--;
}
}
return ans;
}
};