题目描述
可爱的PLY有一个长度为nn的括号序列a[1-n]a[1−n](即仅由字符'('与字符')'组成的字符串)。某一天,PLY的心上人Gay王·齐齐去PLY家玩耍,他对这个括号序列很是喜欢,PLY说只要齐齐能答对一个问题就把这个括号序列送给齐齐当做定情信物:在这个括号序列之中,存在多少个不同的位置pp,只将a[p]a[p]处的括号翻转后(即若原来a[p]a[p]为左括号则变为右括号,原来为右括号则变为左括号),整个括号序列是一个合法的括号序列。齐齐请求你的帮助!
合法括号序列指的是满足以下条件之一的括号序列:
- 空串""是合法括号序列。
- 若"SS"是合法括号序列,则"(S)(S)"也是合法括号序列。
- 若"S_1S1"与"S_2S2"是合法括号序列,则"S_1S_2S1S2"也是合法括号序列。
输入格式
输入第一行11个整数TT表示数据组数。
对于每组数据:
第一行11个整数nn(1 \leq n \leq 10^{5}1≤n≤105)表示括号序列长度。
第二行11个长度为nn的括号序列a[1-n]a[1−n]。
输出格式
对于每组数据,输出11个整数表示满足条件的pp的数目。
样例输入
3 1 ( 2 () 6 (())))
样例输出
0 0 3
题解:若能凑对,则把括号串正向扫描一遍计数,遇到 ( 就+1,遇到 ) 就-1,如果小于0了就说明此串不行,再反向扫描一遍也是同样的原理;
每个括号改变之后重新扫描肯定超时,那就记录下来正向扫描从i到最后的最小值和反向扫描从i到最开始的最小值,反转括号之后扫描值发生变动+2或-2,如果最小值变动后仍不小于0,此串就是可以的。
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<vector>
#include<cstdlib>
#include<algorithm>
using namespace std;
char a[100005];
int pos[100005],neg[100005];
int minzheng[100005],minfan[100005];
int main()
{
int t;
int n;
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
getchar();
int sum1 = 0;
int mmp=0;
//输入并正向扫描
for(int i = 0;i < n;++i)
{
scanf("%c",&a[i]);
if(a[i] == '(')
{
pos[i] = ++sum1;
mmp++;
}
else
pos[i] = --sum1;
}
int minn=1e8;
//正向扫描从i开始的最小值
for(int i = n-1;i>=0;--i)
{
if(pos[i]<minn)
minn=pos[i];
minzheng[i]=minn;
}
//如果是奇数个必然不可以
if(n%2)
{
cout<<"0"<<endl;
continue;
}
//反向扫描
int sum2=0;
for(int i = n-1;i >= 0;--i)
{
if(a[i] == ')')
neg[i] = ++sum2;
else
neg[i] = --sum2;
}
//反向扫描时从i到0的最小值
int minx=1e8;
for(int i = 0;i < n;++i)
{
if(neg[i] < minx)
minx = neg[i];
minfan[i] = minx;
}
char c;
int u;
//如果正反括号数量相等,咋变也白费
if(mmp==n-mmp)
{
cout<<"0"<<endl;
continue;
}
//优化,只能是多的括号变
if(mmp>n-mmp)
{
c='(';
u=-2;
if(mmp-n+mmp!=2)
{
cout<<"0"<<endl;
continue;
}
}
else
{
c=')';
u=2;
if(mmp-n+mmp!=-2)
{
cout<<"0"<<endl;
continue;
}
}
//循环逐个判定
int num=0;
for(int i = 0;i < n;++i)
{
bool judge=1;
if(a[i]!=c)
continue;
if(minzheng[i]+u<0||minfan[i]-u<0)
judge=0;
if(judge)
num++;
}
//输出结果
cout<<num<<endl;
}
return 0;
}