CF17C

传送门

定义一个字符串 \(S\) 的缩字符串 \(S':\)\(S\) 中所有连续的相同字符变成 \(1\) 个。

发现通过复制操作,若 \(A\) 能变成 \(B\),则 \(B'\) 一定是 \(A'\) 的子序列;反之,如果 \(B'\)\(A'\) 的子序列,\(A\) 能复制成 \(B\)

因此我们考虑求出 \(A'\),然后选出 \(B\),同时要满足 \(B'\)\(A'\) 的子序列。

\(dp[i][a][b][c]\) 表示现在 \(B'\) 的下一个字符从 \(A'[i]\) 往后选了,且此时 \(B\) 中分别有 \(a,b,c\)a,b,c

考虑刷表法:

\(B\) 的下一个字符是 a,则必定是由 \(A'\) 的一个字符 a 拓展而来,因此 \(dp[nxt[i][0]][a+1][b][c]\leftarrow dp[i][a][b][c]\);其他两种情况同理。

答案就是 \(\sum dp[1\sim |A'|][...][...][...]\),记得要满足个数差 \(\le 1\) 以及三个个数加起来是 \(n\)

(如果直接这么写会炸空间,但其实 \([a][b][c]\) 三维每一维都不会超过 \((n+2)/3\),可以降 \(27\) 倍空间)

#include <bits/stdc++.h>

using namespace std;
const int MOD = 51123987;

int n, m;
string a;
string a2;
int nxt[155][3];
int dp[155][55][55][55];
int ans = 0;

int main() {
	cin >> n >> a;
	a = ' ' + a;
	for (int i = 1; i <= n; i++)
		if (i == 1 || a[i] != a[i - 1])
			a2 = a2 + a[i];
	m = a2.size();
	a2 = ' ' + a2;
	memset(nxt, 0, sizeof nxt);
	for (int i = m; i >= 1; i--) {
		for (int j = 0; j < 3; j++)
			nxt[i][j] = nxt[i + 1][j];
		if (a2[i] == 'a')
			nxt[i][0] = i;
		if (a2[i] == 'b')
			nxt[i][1] = i;
		if (a2[i] == 'c')
			nxt[i][2] = i;
	}
	
	dp[1][0][0][0] = 1;
	for (int i = 1; i <= m; i++)
		for (int x = 0; x <= (n + 2) / 3; x++)
			for (int y = 0; y <= (n + 2) / 3; y++)
				for (int z = 0; z <= (n + 2) / 3; z++) {
					if (x + y + z == n && abs(x - y) <= 1 && abs(x - z) <= 1 && abs(z - y) <= 1) {
						ans += dp[i][x][y][z];
						ans %= MOD;
					}
					if (nxt[i][0] != 0)
						dp[nxt[i][0]][x + 1][y][z] = (dp[nxt[i][0]][x + 1][y][z] + dp[i][x][y][z]) % MOD;
					if (nxt[i][1] != 0)
						dp[nxt[i][1]][x][y + 1][z] = (dp[nxt[i][1]][x][y + 1][z] + dp[i][x][y][z]) % MOD;
					if (nxt[i][2] != 0)
						dp[nxt[i][2]][x][y][z + 1] = (dp[nxt[i][2]][x][y][z + 1] + dp[i][x][y][z]) % MOD;
				}
	cout << ans << endl;
	return 0;
}
posted @ 2024-03-02 16:53  FLY_lai  阅读(23)  评论(0编辑  收藏  举报