1. 程式人生 > 實用技巧 >Solution -「ARC 110E」Shorten ABC

Solution -「ARC 110E」Shorten ABC

\(\mathcal{Description}\)

  Link.

  給定長度為 \(n\),包含 A, B, C 三種字元的字串 \(S\),定義一次操作為將其中相鄰兩個不相同的字元替換為字符集中不同於這兩個字元的另一種字元。求任意次操作後得到的不同字串個數,答案對 \(10^9+7\) 取模。

  \(n\le10^6\)

\(\mathcal{Solution}\)

  我們希望探究此種替換操作的結合性,trick 為將字符集替換為數字集,將操作表達為數字間的運算。對於本題,令 A, B, C\(1,2,3\),那麼替換操作等價於將相鄰兩數替換為其異或和,於是就能預處理字首異或和來求出一段區間操作後的結果。

  接下來就能 DP 啦,令 \(f(i)\) 表示 \(S\)\(i\) 個字元構成串的答案,列舉操作得到的串的下一個字元即可轉移。最終答案為所有滿足原串中字尾異或和為 \(0\)\(f(i)\) 之和。

\(\mathcal{Code}\)

/* Clearink */

#include <cstdio>

#define rep( i, l, r ) for ( int i = l, rpbound##i = r; i <= rpbound##i; ++i )
#define per( i, r, l ) for ( int i = r, rpbound##i = l; i >= rpbound##i; --i )

const int MAXN = 1e6, MOD = 1e9 + 7;
int n, f[MAXN + 5], sum[MAXN + 5], nxt[MAXN + 5][4];
char s[MAXN + 5];

inline void addeq ( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD, 0 ); }

int main () {
	scanf ( "%d %s", &n, s + 1 );
	bool flg = false;
	rep ( i, 1, n ) if ( ( flg = s[i] != s[1] ) ) break;
	if ( !flg ) return puts ( "1" ), 0;
	rep ( i, 1, n ) sum[i] = sum[i - 1] ^ ( s[i] - 'A' + 1 );
	rep ( j, 0, 3 ) nxt[n][j] = n + 1;
	per ( i, n, 1 ) {
		rep ( j, 0, 3 ) nxt[i - 1][j] = nxt[i][j];
		nxt[i - 1][sum[i]] = i;
	}
	f[0] = 1;
	int ans = 0;
	rep ( i, 0, n - 1 ) {
		rep ( j, 0, 3 ) if ( sum[i] ^ j ) {
			addeq ( f[nxt[i][j]], f[i] );
		}
		if ( sum[i + 1] == sum[n] ) addeq ( ans, f[i + 1] );
	}
	printf ( "%d\n", ans );
	return 0;
}