【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数目相同
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT5148.html