• 【AT5148】[AGC036E] ABC String(乱搞)


    点此看题面

    • 给定一个由"A","B","C"构成的字符串(s),要求选出一个最长的子序列,使得三种字符出现次数相同且相邻字符不同。
    • (|s|le10^6)

    乱搞题

    首先,连续一段相同的字符肯定不可能同时选择,我们可以先把它们压成一个。

    假设此时出现次数由少到多分别是(A,B,C)

    我们想要让(C)的出现次数和(B)相同,第一种方式是直接删去,即把两端字符不相同的(C)全部删掉;第二种方式是和一个(A)一起删去,要求(AC)(CA)的两端不能全为(B)(实际上没有这种情况,如果形成(BACB),这个(C)在第一步中就会被删去)。

    由于(C)的出现次数比(B)多,所以(C)不可能只与(B)相邻,相连的(AC)肯定存在,因此一定能通过这种方式使得(C)(B)出现次数相同。

    然后我们想要保持(B,C)数量之差不变,让它们的出现次数都变成(A),只要每次删去一对(BC),要求两端不全为(A)即可。

    代码:(O(|s|))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 1000000
    using namespace std;
    int n,c[3];char s[N+5];
    I void Del(char A,char B,char C)//删去相连的AB,使得B和C数量相同
    {
    	RI i=1,l=n;n=0;W(c[B-'A']^c[C-'A'])//B和C数量仍然不同
    		(s[i]==A&&s[i+1]==B||s[i]==B&&s[i+1]==A)&&(s[n]^C||s[i+2]^C)?(--c[A-'A'],--c[B-'A'],i+=2):(s[++n]=s[i++]);//AB相连且两端不为C时才能删去
    	W(i<=l) s[++n]=s[i++];s[n+1]=0;//把剩余部分存回来
    }
    int main()
    {
    	RI i,l;for(scanf("%s",s+1),l=strlen(s+1),i=1;i<=l;++i) s[n]^s[i]&&++c[(s[++n]=s[i])-'A'];s[n+1]=0;//连续字符压成一个
    	char A=c[0]<=c[1]&&c[0]<=c[2]?'A':(c[1]<=c[2]?'B':'C'),C=c[2]>=c[0]&&c[2]>=c[1]?'C':(c[1]>=c[0]?'B':'C'),B='A'+'B'+'C'-A-C;//按出现次数排序
    	for(l=n,n=0,i=1;i<=l&&c[C-'A']^c[B-'A'];++i) s[i]==C&&(!n||i==l||s[n]^s[i+1])?--c[C-'A']:s[++n]=s[i];//直接删去两端字符不同的C
    	W(i<=l) s[++n]=s[i++];s[n+1]=0;return Del(A,C,B),Del(B,C,A),puts(s+1),0;//删去AC使BC数目相同,删去BC使ABC数目相同
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    学习八数码拓展
    jzoj4762. 千帆渡
    jzoj5354. 导弹拦截
    学习上下界网络流小记
    jzoj2702. 探险&jzoj3917. 【NOIP2014模拟11.2A组】福慧双修
    jzoj100048. 紧急撤离
    jzoj100045. 好数
    jzoj3327. 陶陶的难题
    2368. 黑白棋
    学习类欧几里得小记
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT5148.html
Copyright © 2020-2023  润新知