Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)/CF1323 C. Unusual Competitions(栈/括号匹配)
A bracketed sequence is called correct (regular) if by inserting "+" and "1" you can get a well-formed mathematical expression from it. For example, sequences "(())()", "()" and "(()(()))" are correct, while ")(", "(()" and "(()))(" are not.
The teacher gave Dmitry's class a very strange task — she asked every student to come up with a sequence of arbitrary length, consisting only of opening and closing brackets. After that all the students took turns naming the sequences they had invented. When Dima's turn came, he suddenly realized that all his classmates got the correct bracketed sequence, and whether he got the correct bracketed sequence, he did not know.
Dima suspects now that he simply missed the word "correct" in the task statement, so now he wants to save the situation by modifying his sequence slightly. More precisely, he can the arbitrary number of times (possibly zero) perform the reorder operation.
The reorder operation consists of choosing an arbitrary consecutive subsegment (substring) of the sequence and then reordering all the characters in it in an arbitrary way. Such operation takes ll nanoseconds, where ll is the length of the subsegment being reordered. It's easy to see that reorder operation doesn't change the number of opening and closing brackets. For example for "))((" he can choose the substring ")(" and do reorder ")()(" (this operation will take 22 nanoseconds).
Since Dima will soon have to answer, he wants to make his sequence correct as fast as possible. Help him to do this, or determine that it's impossible.
The first line contains a single integer nn (1≤n≤1061≤n≤106) — the length of Dima's sequence.
The second line contains string of length nn, consisting of characters "(" and ")" only.
Print a single integer — the minimum number of nanoseconds to make the sequence correct or "-1" if it is impossible to do so.
8 ))((())(
6
3 (()
-1
大意是给一个括号序列,可以进行无数次的reorder操作使得该序列括号匹配合法。对于每次reorder操作,可以使得[l,r]区间内的括号任意调换顺序(注意不是改变左右括号数目),花费的时间为r-l+1,问要令括号匹配合法的话最少的花费是多少。
首先可以知道,对于每次reorder选择的区间,里面的左右括号数目一定得是相等的,这样这次操作才有意义。可以这么想,不妨假设有这么一段序列:(.... 四个点代表一段左右括号数目相等的区间,这样这整个区间的所有括号数目就不相等。对其进行reorder后四个点代表的区间合法了,而单独的那个左括号要么遇到右括号(这样的话不如直接reorder四个点的区间),要么遇到左括号(仍然不合法),所以这样是没有意义的。(玄学理解下)。
所以首先用前缀和的形式判断区间左右括号是否相等,然后利用传统的括号匹配的思想,pos代表当前可能需要reorder的区间的左端点,遇到一个新括号时如果栈为空,直接入栈且把pos设置为i,非空的话首先看能否对栈顶括号和当前括号进行匹配,能的话删栈顶,如果此时栈空了就把pos设置为i+1;如果不能匹配则直接把当前括号入栈。这时检查pos到i这段区间的左右括号数目是否相等,相等且栈非空(有东西可删)的话更新答案然后清空栈,重置pos。
另外注意一点,一段大的非法区间里包含一段小的合法区间的话,还是得对整个大的区间reorder。
#include <bits/stdc++.h> using namespace std; int n; char a[1000005]; int sum[1000005]={0};//左括号为1,右括号为-1 stack<char>s; int ans=0; int main() { cin>>n; int i; scanf("%s",a); int pos=1; for(i=1;i<=n;i++) { if(a[i-1]=='(') { sum[i]=1; } else { sum[i]=-1; } if(i==1) { s.push(a[0]); continue; } sum[i]+=sum[i-1]; if(s.size()==0) { pos=i; s.push(a[i-1]); continue; } if(s.top()=='('&&a[i-1]==')') { s.pop(); if(s.size()==0)pos=i+1; } else { s.push(a[i-1]); } if(sum[i]-sum[pos-1]==0&&s.size()) { ans+=(i-pos+1); pos=i+1; while(s.size())s.pop(); } } if(sum[n]-sum[0]!=0)cout<<-1<<endl; else cout<<ans<<endl; return 0; }