翻转一个括号使得所有括号都能配对,问一共可以翻转几个括号。
AC
方法1:
-
如果某个位置上的右括号数量大于他的左括号的数量那么这个位置及之后的位置无论如何翻转都不能满足配对。
例如:())((() 第三个位置:右括号2个,左括号1个,位置3之后的括号无论如何变化,第三个位置上的状态都不会改变。 -
同理如果从右边开始考虑,如果某个位置上的右括号数量小于左括号数量,之后位置上的括号也是不能匹配
例如:((()(() 第五个位置:右括号1个,左括号两个。那么位置5之前的括号无论如何变化,位置5的状态都不会改变。 -
为什么考虑左右的不考虑当下的?因为:虽然当前位置不符合匹配的要求“(()”,但是我可以通过翻转改变呀。
-
用L,R数组记录括号前后缀数量和,之后枚举每个括号,判断能否翻转使得匹配
方法2:
- 要想翻转一个使得整体括号匹配,只能多出来2个左括号或者2个右括号
- 假如多2个左括号,我们最左边开始判断,如果当前位置为左括号且左括号的前缀和大于等于2,那么翻转这个位置就可以使得整个括号匹配。
- 假如多2个右括号,我们最左边开始判断,如果当前位置为右括号且左括号的前缀和大于等于-2,那么翻转这个位置就可以使得整个括号匹配。
- 当我们求完前缀和之后,需要从后向前更新最小值,这样能保证翻转前面的区间的同时后面的区间也能满足。
#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define REP(i, n) for (int i = 1; i <= (n); ++i)
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define N 1000005
using namespace std;
int L[N], R[N];
int Fl[N], Fr[N];
char s[N];
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int n;
while (scanf("%d%s", &n, s+1) != EOF) {
Fl[0] = 1;
Fr[n+1] = 1;
mem(L, 0);
mem(R, 0);
// 求左括号前缀和,并判断当前位置是否可以翻转
for (int i = 1; i <= n; ++i) {
if (s[i] == '(') L[i] = L[i-1] + 1;
else L[i] = L[i-1] - 1;
Fl[i] = (Fl[i-1] && L[i] >= 0);
}
// 求右括号后缀和,并判断当前位置是否可以翻转
for (int i = n; i >= 1; --i) {
if (s[i] == ')') R[i] = R[i+1] + 1;
else R[i] = R[i+1] - 1;
Fr[i] = (Fr[i+1] && R[i] >= 0);
}
int ans = 0;
for (int i = 1; i <= n; ++i) {
if (Fl[i-1] == 0) {
break;
}
if (Fr[i+1] == 0) {
continue;
}
if (s[i] == ')' && L[i-1] + 1 == R[i+1]) ans++;
else if (s[i] == '(' && L[i-1] == R[i+1] + 1) ans++;
}
cout << ans << endl;
}
return 0;
}
#include <bits/stdc++.h>
#define LL long long
#define P pair<int, int>
#define lowbit(x) (x & -x)
#define mem(a, b) memset(a, b, sizeof(a))
#define REP(i, n) for (int i = 1; i <= (n); ++i)
#define rep(i, n) for (int i = 0; i < (n); ++i)
#define N 1000005
using namespace std;
int L[N];
char s[N];
int main() {
#ifndef ONLINE_JUDGE
freopen("in.txt", "r", stdin);
#endif
int n;
while (scanf("%d%s", &n, s+1) != EOF) {
REP (i, n) L[i] = L[i-1] + (s[i] == '(' ? 1 : -1);
if (L[n] != -2 && L[n] != 2) {
puts("0");
continue;
}
for (int i = n-1; i >= 1; --i) L[i] = min(L[i], L[i+1]);
int cnt = 0;
int ans = 0;
REP (i, n) {
if (s[i] == '(') {
if (L[i] >= 2 && L[n] == 2) ans++;
cnt++;
}else{
if (L[i] >= -2 && L[n] == -2) ans++;
cnt--;
}
// 如果右括号已经比左括号 后面就不会有解
if (cnt < 0) break;
}
printf("%d\n", ans);
}
return 0;
}