题意:
给你一个括号串,你可以交换任意两个位置(可以相同)的括号,使得这个串在向右循环移位变成平衡串的次数最多。问你次数以及交换哪两个位置。
题解:
这道题有点东西,首先我们可以知道,如果左右括号数量不相同无法进行循环移位消去。如果要循环移位的话,一定是从最后一个不可被消去的右括号+1位置开始的。
然后如果要交换位置,一定是两个位置相同或者一个是左括号,一个是右括号,否则就毫无意义。然后我们可以发现,交换位置是为了能够增加完美匹配的节点数量,比如
)((()())
这个串我们会交换第3和最后一个点的位置,这样能够增加中间两个小括号的匹配。
所以我们要记录每一个右括号以及与其匹配的左括号之间的子括号的数量,但是子括号的子括号是不被记录答案的:( (()()) ())
像这个,最外面的括号的子括号是2.
还有要注意的一点是,能够让答案增大的位置,只有从最后一个未匹配的右括号+1位置开始的地方算起,左括号比右括号数量相同或者大1的地方:
)…(…(
比如说上面这种,如果在第二个右括号后面,并且无法将这个括号匹配掉的地方去交换的话,要么会将第二个括号消掉,从而无法增加循环移位的数量,要么会将最后一个无法匹配的左括号的位置右移,导致情况变劣。因为变位置会影响原来的完美匹配的字符串,导致右括号失配。
那么我们对于中间的匹配,也就是最后一个无法匹配的右括号的右边到第一个不能匹配的左括号的左边的情况,设其为sum。如果是在深度为0(完美匹配)的时候,我们需要知道当前位置变换之后增加的数量。以及如果是深度为1(左括号比右括号多一个)的时候,我们需要增加sum,因为现在多出来的是独立于原来算在答案里的,举个例子:
)(()()())(
他的sum为2,因为只有1,9两个循环移位。
在位置为9的时候,add[9]为4,此时不能加sum,因为它原本是算到sum中的。
但是对于
)((()()()))(
他的sum为2,
在位置为11的时候,add[11]为4,但是此时深度为1,说明它变换之后是不记在原来的答案之中的,所以需要加上sum。
#include<bits/stdc++.h>
using namespace std;
const int N=6e5+5;
char s[N];
int st[N],num[N],add[N],dep[N],lef[N],top;
int main()
{
int n;
scanf("%d",&n);
scanf("%s",s+1);
for(int i=1;i<=n;i++)
s[i+n]=s[i];
int en=0,l=0,r,bal=0;
for(int i=1;i<=n;i++){
if(s[i]=='(')l++,bal++;
else{
if(l)l--;
else en=i;
bal--;
}
}
l=r=1;
if(bal)
return 0*printf("0\n1 1\n");
int sum=0;
for(int i=en+1;i<=en+n;i++){
if(s[i]=='(')
st[++top]=i;
else{
add[i]=num[top]+1;
lef[i]=st[top];
num[top--]=0;
num[top]++;
dep[i]=top;
}
if(!top)
sum++;
}
int ans=sum;
for(int i=en+1;i<=en+n;i++){
if(s[i]==')'){
if(!dep[i]&&add[i]>ans)
ans=add[i],l=lef[i],r=i;
if(dep[i]==1&&add[i]+sum>ans)
ans=add[i]+sum,l=lef[i],r=i;
}
}
printf("%d\n%d %d\n",ans,(l-1)%n+1,(r-1)%n+1);
return 0;
}