Description
给出一个括号序列,问其子串中合法括号序列长度最大值,以及该长度的子串中合法括号序列数量
Input
输入一个长度不超过 106 10 6 的括号序列
Output
输出子串中合法括号序列长度最大值,以及该长度的子串中合法括号序列数量,如果子串中没有合法括号序列则输出 0 1 0 1
Sample Input
)((())))(()())
Sample Output
6 2
Solution
把左括号看作 1 1 ,右括号看作,那么该字符串对应一个 −1,1 − 1 , 1 组成的序列,对该序列求一个前缀和 s[1],...,s[n] s [ 1 ] , . . . , s [ n ] 后,易知如果区间 [l,r] [ l , r ] 为一个合法括号序列,那么首先区间 [l,r] [ l , r ] 中 s s 序列的最小值应该不小于,否则说明从 l l 到取最小值的位置,右括号数量比左括号数量多,显然不合法,且 s[r]=s[l−1] s [ r ] = s [ l − 1 ] ,表示区间内左括号数量等于右括号数量
考虑从第 i i 个字符开始的子串作为合法括号序列的最长长度,根据上面的分析,该合法括号序列的右端点首先需要满足 s[j]=s[i−1] s [ j ] = s [ i − 1 ] ,对 s s 序列预处理,把值相同的值用链表串起来以便快速从一个位置跳到下一个和其值相同的位置,然后需要满足 s[i],...,s[j] s [ i ] , . . . , s [ j ] 中最小值不小于 s[i−1] s [ i − 1 ] ,同样用 ST S T 表预处理区间最小值以便快速查询,那么固定 i i 就可以很快的找到从开始的合法括号序列最长长度,以此更新答案即可,时间复杂度 O(nlog2n) O ( n l o g 2 n )
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1000005;
int dp[maxn][21],a[maxn],n,nxt[maxn],vis[maxn];
char c[maxn];
void ST()
{
for(int i=1;i<=n;i++)dp[i][0]=i;
for(int j=1;(1<<j)<=n;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
{
int k=i+(1<<(j-1));
if(a[dp[i][j-1]]<a[dp[k][j-1]])dp[i][j]=dp[i][j-1];
else dp[i][j]=dp[k][j-1];
}
}
int query(int l,int r)
{
int k=(int)(log(1.0*(r-l+1))/log(2.0));
return min(a[dp[l][k]],a[dp[r-(1<<k)+1][k]]);
}
bool check(int d)
{
for(int i=1;i<=n-d+1;i++)
{
if(a[i+d-1]==a[i-1]&&query(i,i+d-1)>=a[i-1])
return 1;
}
return 0;
}
map<int,int>M;
int main()
{
scanf("%s",c+1);
n=strlen(c+1);
a[0]=0;
M[0]=0;
memset(nxt,-1,sizeof(nxt));
for(int i=1;i<=n;i++)
{
if(c[i]=='(')a[i]=a[i-1]+1;
else a[i]=a[i-1]-1;
if(M.find(a[i])!=M.end())nxt[M[a[i]]]=i;
M[a[i]]=i;
}
ST();
int len=0,ans=1;
memset(vis,0,sizeof(vis));
for(int i=0;i<=n;i++)
if(!vis[i])
{
int j=i;
while(nxt[j]!=-1)
{
if(query(j+1,nxt[j])>=a[j])vis[j]=1,j=nxt[j];
else break;
}
if(j-i>len)len=j-i,ans=1;
else if(j-i==len)ans++;
}
if(len==0)printf("0 1\n");
else
{
int ans=0;
for(int i=1;i<=n-len+1;i++)
if(a[i+len-1]==a[i-1]&&query(i,i+len-1)>=a[i-1])ans++;
printf("%d %d\n",len,ans);
}
return 0;
}