D. Bracket Coloring

题目链接:Problem - 1837D - Codeforces

题意理解:

判断是否可以将字符串s分成若干字子串,使每个子串或每个翻转的子串是合法的括号序列。

预备知识:

首先要知道如何判断一个括号序列是否是合法,共有两个条件:

1.左括号数=右括号数

2.对于任意位置i,i前的左括号数一定大于等于右括号数(即右括号数不大于左括号数)

解题思路:

这个字符串最终一定会分为两个部分,一部分是不翻转就合法的,另一部分是需要翻转才合法的(每部分都可能为空)。

思路一:

首先判断整个字符串是否是一个合法的括号序列,如果他不是,输出-1;

对于每一个位置,那么如果前面左括号多,那么将它放到无需翻转的子串中,否则,放到需要翻转的子串中。

但是感觉这种思路的合理性证明不好解释,又用了思路二。

代码:

#include<iostream>
using namespace std;

int T,n;
string s;
int ans[200005];

bool check()
{
    int l=0,r=0;
    for(int i=0;i<n;i++)
    {
        if(s[i]=='(') l++;
        else r++;
    }

    if(l!=r) return  false;
    return true;
}
void solve()
{
    cin>>n>>s;

    //先进行合法性检验
    if(!check())
    {
        cout<<"-1"<<endl;
        return;
    }

    //初始化变量
    int l=0,r=0;
    for(int i=0;i<n;i++) ans[i]=0;

    //先找一段最长的RBS序列
    for(int i=0;i<n;i++)
    {
        if(s[i]=='(')
        {
            if(l>=r) ans[i]=1;
            else ans[i]=2;
            l++;
        }
        else
        {
            r++;
            if(l>=r) ans[i]=1;
            else ans[i]=2;
        }
    }

    int cnt=1;
    for(int i=1;i<n;i++)
    {
        if(ans[i]!=ans[0]) cnt=2;
    }

    cout<<cnt<<endl;
    if(cnt==1) for(int i=0;i<n;i++) cout<<"1 ";
    else for(int i=0;i<n;i++)
    {
        cout<<ans[i]<<" ";
    }
    cout<<endl;
}

int main()
{
    //freopen("D:\\data.txt","r",stdin);

    cin>>T;
    while(T--)
    {
        solve();
    }

    return 0;
}

思路二:

首先判断这个序列1.是否合法?2.是否可以用一种颜色填充。

合法:左括号数等于右括号数

一种颜色填充:保证任意位置左括号数始终大于右括号数(整个字符串是一个合法括号序列) 或者 保证任意位置右括号数始终大于左括号数(将整个字符串翻转,是一个合法括号序列)

否则,他就一定需要两种颜色填充。

那么就要找一个最长合法子串,开一个栈S,对于每个位置:

如果是左括号,那么就直接压进去;

如果是右括号,那么判断栈顶是否为左括号,如果是左括号,那么就与栈顶的左括号匹配,一起出栈;否则,他前面就没有左括号了,他就是需要翻转位置。

到最后,栈一定是栈顶都是左括号,栈底都是右括号,如:“)))(((”这样的形式。

将这些位置填充为颜色2

代码:

#include<iostream>
#include<stack>
using namespace std;

const int N = 200005;
int n,T;
string s;
int ans[N];
stack<int>S;

void solve()
{
    cin>>n>>s;

    //先判断两个问题:1.是否合法    2.是否可以用一种颜色填充
    int l=0,r=0;
    bool sv1=true,sv2=true;
    for(int i=0;i<n;i++)
    {
        if(s[i]=='(') l++;
        else if(s[i]==')') r++;

        if(r>l) sv1=false;
        if(l>r) sv2=false;
    }

    if(l!=r)
    {
        cout<<"-1"<<endl;
        return;
    }
    else if(sv1 || sv2) 
    {
        cout<<"1"<<endl;
        for(int i=0;i<n;i++) cout<<"1 ";
        cout<<endl;
        return;
    }

    for(int i=0;i<n;i++)
    {
        ans[i]=1;
        if(s[i]=='(') S.push(i);
        else
        {
            if(!S.empty() && s[S.top()]=='(') S.pop();
            else S.push(i);
        }
    }

    while(!S.empty())
    {
        ans[S.top()]=2;
        S.pop();
    }

    cout<<"2"<<endl;
    for(int i=0;i<n;i++) cout<<ans[i]<<" ";
    cout<<endl;
}

int main()
{
    //freopen("D:\\data.txt","r",stdin);

    cin>>T;
    while(T--) solve();

    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值