Codeforces Round #529 (Div. 3) E. Almost Regular Bracket Sequence

题目大意就是给一个长度为n括号序列,然后问你有几个位置,你将该位置的括号反转之后,这个括号序列可以变成合法的括号序列。合法的括号序列定义就是 1.括号序列里左右括号数量相等 2.对于i位置对应的前缀里左括号数量>=右括号数量(1<=i<=n)

先设左括号为1,右括号为-1,然后求出括号序列对应的前缀和数组a

假设要将pos位置的左括号改成右括号是满足条件的。

那么必有

              1.未修改前左括号个数比右括号个数多2

              2.未修改前 a[i]>=0  (1<=i<=pos-1),可以等效成min(a[i] (1<=i<=pos-1))>=0

              3.修改之后 a[i]>=0  (pos<=i<=n)

仅仅用这三个条件是无法在题目要求时间内求出答案的,因为第3个条件的判断需要把a[i] (pos<=i<=n)都减2然后再判断是否全都>=0,这样的话时间复杂度很高,所以这里需要优化,因为把pos位置的左括号变成右括号,会对pos位置的前缀值造成-2的作用,当然pos+1到n位置的前缀因为包含了pos位置,所以pos+1到n位置的前缀值也都会-2,总的来说就是a[i]会减小2  (pos<=i<=n)

,然后要求减小后>=0,那么就要a[i]>=2,然后是pos到n范围的前缀值a都满足,那么就等效成min(a[i]  (pos<=i<=n))>=2.

这样判断pos位置的括号能否反转就仅仅和原数组a有关了,我们只需预处理出前缀数组a的前缀最小值数组premin,和前缀数组a的后缀最小值数组sufmin,就可以对pos位置O(1)判断了,因此总复杂度O(N)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int a[maxn],premin[maxn],sufmin[maxn];
char s[maxn];
int main()
{
    int n,tmp=0,ans=0,low=0;
    scanf("%d",&n);
    premin[0]=sufmin[n+1]=maxn;
    for(int i=1;i<=n;i++)
    {
        scanf(" %c",&(s[i]));
        if(s[i]=='(')
            tmp+=1;
        else
            tmp-=1;
        a[i]=tmp;
        premin[i]=min(a[i],premin[i-1]);
    }
    for(int i=n;i>=1;i--)
    {
        sufmin[i]=min(a[i],sufmin[i+1]);
    }
    if(n&1||(tmp!=-2&&tmp!=2))
    {
        printf("0\n");
        return 0;
    }
    /*
    for(int i=1;i<=n;i++)
        cout<<a[i]<<"   ";
    cout<<endl<<endl;
    for(int i=1;i<=n;i++)
        cout<<premin[i]<<"  ";
    cout<<endl<<endl;
    for(int i=1;i<=n;i++)
        cout<<sufmin[i]<<"  ";
    cout<<endl<<endl;
    */
    if(tmp==2)
        low=2;
    else
        low=-2;
    for(int i=1;i<=n;i++)
    {
        if(tmp==2&&s[i]=='(')
        {
            if(premin[i]>=0&&sufmin[i]>=low)
                ans++;
        }
        else
        {
            if(tmp==-2&&s[i]==')')
            {
                if(i==1&&sufmin[i]>=low)
                    ans++;
                if(i!=1&&premin[i-1]>=0&&sufmin[i]>=low)
                    ans++;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值