定义一个字符串 \(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;
}
标签:nxt,...,int,CF17C,55,a2,dp
From: https://www.cnblogs.com/FLY-lai/p/18048849