ARC110E - Shorten ABC
一个由\(ABC\)组成的字符串,每次找相邻的两个不同的字符,将它们替换成那个和它们都不想同的字符(如\(AB\to C\))。
问若干次操作能够形成的字符串的方案数。
\(n\le 10^6\)
类似于AGC027E。
然而还是做不出来。
令\(A,B,C\)分别为\(1,2,3\)。可以发现每次操作之后异或和不变。
并且可以发现如果出现了\(AA\)的情况,可以将它消掉(除非全部相等)。
于是可以发现一段字符串形成一个字符的充分必要条件是:异或和相等,字符不全相等(长度为\(1\)的除外)。
先把全部相等的特判掉。
对于一个最终形成的字符串,每个字符匹配原串的一个子串。
搞个DP,转移的时候找最小的一段。可以发现找最小的一段可以保证字符不全相等。
统计答案的时候,如果剩下的一段异或和为\(0\),那么就计入答案中。因为异或和为\(0\),肯定有办法将后面这些消掉。
(或者也可以这样:如果剩下一段异或和不为\(0\)就计入答案中。相当于最后一个字符特殊处理。如果这一段全部相等,它实际上可以和前面的互相消。因为特判了全部相同的情况,所以合法的方案总是存在的。)
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 1000005
#define mo 1000000007
#define ll long long
int n;
char str[N];
int a[N],s[N];
int lst[N][4];
int f[N];
int main(){
freopen("in.txt","r",stdin);
scanf("%d%s",&n,str+1);
for (int i=1;i<=n;++i){
a[i]=str[i]-'A'+1;
s[i]=s[i-1]^a[i];
}
bool same=1;
for (int i=1;i<n;++i)
same&=(a[i]==a[i+1]);
if (same || n==1){
printf("1\n");
return 0;
}
lst[n+1][0]=lst[n+1][1]=lst[n+1][2]=lst[n+1][3]=n+1;
for (int i=n;i>=0;--i){
memcpy(lst[i],lst[i+1],sizeof lst[i]);
lst[i][s[i+1]]=i+1;
}
f[0]=1;
ll ans=0;
for (int i=0;i<n;++i)
for (int c=1;c<=3;++c)
(f[lst[i][c^s[i]]]+=f[i])%=mo;
for (int i=1;i<=n;++i)
if ((s[n]^s[i])==0)
ans+=f[i];
/*
for (int i=0;i<n;++i)
if (s[n]^s[i])
ans+=f[i];
*/
ans%=mo;
printf("%lld\n",ans);
return 0;
}