把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【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数目相同
}
posted @ 2021-05-19 14:02  TheLostWeak  阅读(56)  评论(0编辑  收藏  举报