ABC350-F

ABC350-F

题意

给一个包含左右括号的字符串,字符串的每个右括号,都有与之匹配的左括号,对于每一对匹配的括号组,假设左括号的位置为l,右括号的位置为r,且l和r之间无括号,那么可以将[l + 1, r - 1]区间内的字符串顺序逆转,且区间内的大写字母变小写,小写字母变大写,并且把这对括号从字符串中删去。问最后的字符串为什么。

分析

这类括号匹配的问题很容易让我们想到用栈维护左括号的位置,那么每当我们遇到一个右括号,就可将栈顶的左括号取出,且与之配对。

假设 i i i位置的字符为字母,且它的最终位置为 p o s i pos_i posi,如果它被两个括号包围着,且离它最近的左右括号的位置为 l 1 , r 1 l_1,r_1 l1,r1,离他较远的左右括号位置为 l 2 , r 2 l_2,r_2 l2,r2,

首先当 r 1 位置的右括号与 l 1 位置的左括号匹配时 , p o s i = ( l 1 + r 1 ) − i r_1位置的右括号与l_1位置的左括号匹配时, pos_i = (l_1 + r_1) - i r1位置的右括号与l1位置的左括号匹配时,posi=(l1+r1)i

然后当 r 2 位置的右括号与 l 2 位置的左括号匹配时 , p o s i = ( l 2 + r 2 ) − ( l 1 + r 1 ) + i r_2位置的右括号与l_2位置的左括号匹配时, pos_i = (l_2 + r _ 2) - (l_1 + r_1) + i r2位置的右括号与l2位置的左括号匹配时,posi=(l2+r2)(l1+r1)+i

其实很容易发现,如果 i i i位置上的字母被奇数个括号包围时, p o s i 最终一定会 − i pos_i最终一定会-i posi最终一定会i,不然 p o s i 一定会 + i pos_i一定会+i posi一定会+i,

如果一对括号(左右括号位置分别为l,r)被偶数个括号包围着,那这一对括号包围的字母最终的位置一定会 +(l + r),不然一定会 -(l + r), 这个可以用树状数组或者差分进行区间操作

  • 所以我们遍历整个字符串,看看每个位置前的左括号数量,如果是奇数个,那么将该字符的最终位置 p o s i − i pos_i - i posii且最后的大小写形式(如果该位置是字母的话)与现在一样,否则 p o s i + i pos _i + i posi+i,且大小写形式逆转(如果是字母)
  • 如果遇到左括号,就将其位置放入栈中
  • 如果遇到右括号,就将栈顶左括号位置取出
  • 假设当前左右括号位置分别为l,r
  • 如果这对括号前,还有奇数个括号,那么区间[l + 1,r - 1]中所有的字母的最终位置-(l + r), 不然就 + (l + r)

赛后过的赛事的丑陋代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<climits>
#include<map>
#include<unordered_map>
#include<set>
#include<unordered_set>
#include<bitset>
#include<cmath>
#ifdef ONLINE_JUDGE
#define debug(...) 0
#else
#include "debug.h"
#endif

#define x first 
#define y second 
#define ALL(a) a.begin(),a.end()
using namespace std;

typedef pair<int,int> PII;
typedef pair<PII,int> PIII;
typedef long long LL;
const int N = 5e5 + 10;
int tr[N],n;
stack<int> stk;
string s;

int lowbit(int x)
{
    return x & -x;
}

void add(int x,int c)
{
    for(int i = x; i <= n; i += lowbit(i)) tr[i] += c;
}

int sum(int x)
{
    int res = 0;
    for(int i = x; i; i -= lowbit(i)) res += tr[i];
    return res;
}

void solve()
{
    cin >> s;
    n = s.size();
    s = '?' + s;
    for(int i = 1; i <= n; i++) 
    {
        if(stk.size() % 2)
        {
            if(s[i] >= 'A' && s[i] <= 'Z') s[i] = s[i] - 'A' + 'a';
            else if(s[i] >= 'a' && s[i] <= 'z') s[i] = s[i] - 'a' + 'A';
            add(i,i * -1);
            add(i + 1, i);
        } 
        else 
        {
            add(i, i);
            add(i + 1, i * -1);
        }
        if(s[i] == '(' || s[i] == ')')
        {
            if(s[i] == '(') stk.push(i);
            else 
            {
                int l = stk.top() + 1; stk.pop();
                int r = i - 1;
                if(l > r) continue;
                if(stk.size() % 2)
                {
                    add(l,(l + r) * -1);
                    add(r + 1,l + r);
                }
                else 
                {
                    add(l, l + r);
                    add(r + 1,(l + r) * -1);
                }
            }
        }
    }
    string res(n + 1,' ');
    for(int i = 1; i <= n; i++) 
    {
        int x = sum(i);
        if(x > 0 && x <= n && s[i] != '(' && s[i] !=')') res[x] = s[i];
    }
    for(int i = 1; i <= n; i++) 
        if(res[i] != ' ') cout << res[i];
}


int main()
{
    int t = 1;
    while(t--) solve();
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值