Loading

CF17C Balance (字符串 dp)

CF17C Balance

upd 2024.11.13

字符串 dp

可以看到答案要求的是个数,我们的计数工具有 dp,组合计数等。这题中的操作可以说是“覆盖”,偏简单,又看到数据范围偏小,可以想想用朴素的 dp 怎么做。

计数平衡串,关心字母的数量,字符集又很小,所以可以想直接表示在状态里就好了。

想想操作的本质,它并不改变相对顺序,于是可以看成选出原串中的若干字符进行扩展。这就是从左到右线性 dp。

由于相邻相同字符会算重怎么办?压缩一下就好了。

发送式转移比较简单,预处理每个位置的转移位置就好了。

考虑 dp。观察题目的操作,可以发现一些性质:

  1. 不改变字符出现的相对顺序
  2. 相当于覆盖操作,可以盖掉某字符

那么我们就可以考虑将字符串”压缩“,即将相同字母的区间压缩为一个。那么就可以 dp 了,考虑设 \(f_{i,a,b,c}\) 表示匹配到压缩串第 \(i\) 个位置用了 \(a\)a\(b\)b\(c\)c 的方案数。

转移枚举下一个字符,预处理 \(nxt_{i,j}\) 表示压缩串中第 \(i\) 个字符后第一个字符 \(j\) 的位置。

复杂度 \(O(n^4)\),后面三维最大才 \(\frac{n}{3}\),所以只会更快。

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 152, mod = 51123987;
int n, m;
char s[N], A[N];
int nxt[N][3];
int f[N][52][52][52], ans;
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n >> s + 1;
	int lim = n / 3 + 1;

	for(int i = 1; i <= n; i++) {
		if(i == n || s[i] != s[i + 1]) {
			A[++m] = s[i];
		}
	}
	
	nxt[m + 1][0] = nxt[m + 1][1] = nxt[m + 1][2] = m + 1;
	for(int i = m; i >= 0; i--) {
		for(int j = 0; j < 3; j++) {
			nxt[i][j] = nxt[i + 1][j];
		}
		nxt[i][A[i] - 'a'] = i;
	}
	f[0][0][0][0] = 1;
	for(int i = 0; i <= m; i++) {
		for(int a = 0; a <= lim; a++) {
			for(int b = 0; b <= lim; b++) {
				for(int c = 0; c <= lim; c++) {
					(f[nxt[i][0]][a + 1][b][c] += f[i][a][b][c]) % mod;
					(f[nxt[i][1]][a][b + 1][c] += f[i][a][b][c]) % mod;
					(f[nxt[i][2]][a][b][c + 1] += f[i][a][b][c]) % mod;
				}
			}
		}
	}
	for(int i = 1; i <= m; i++) {
		for(int a = 0; a <= lim; a++) {
			for(int b = 0; b <= lim; b++) {
				for(int c = 0; c <= lim; c++) {
					f[i][a][b][c] = (f[i][a][b][c] + f[i - 1][a][b][c]) % mod;
				}
			}
		}
	}
	for(int i = 0; i <= lim; i++) {
		for(int j = 0; j <= lim; j++) {
			for(int k = 0; k <= lim; k++) {
				if(i + j + k == n && abs(i - j) <= 1 && abs(j - k) <= 1 && abs(i - k) <= 1) (ans += f[m][i][j][k]) % mod;
			}
		}
	}
	std::cout << ans << "\n";
	return 0;
}
posted @ 2024-06-28 20:46  Fire_Raku  阅读(12)  评论(0编辑  收藏  举报