Codeforces Round #529 (Div. 3) E. Almost Regular Bracket Sequence (括号配对,前缀和)

在这里插入图片描述

翻转一个括号使得所有括号都能配对,问一共可以翻转几个括号。

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值