题解:AT_agc066_c [AGC066C] Delete AAB or BAA

题意:给出一个字符串,每次可以删除一个 AAB 或者 BAA,求最多能删除多少次。

做法:

agc 的题都要猜结论,我们考虑什么样的串可以全删,直接打表。

首先我们发现 A 的数量一定是 B 的两倍,这个显然是必要条件,但是很显然不是充分的,比如 ABA 这个串就爆炸了,所以我们猜两端不能相等,但是 AABBAA 这样的串又可以消除。

但是我们发现,AABBAA 这样的串我们可以分成 AAB 和 BAA 这样的两个串分别删除,不需要一次删除,所以不考虑也是正确的。

我们考虑证明这个结论:只要一个串满足 A 的个数是 B 的两倍且两端不同就可以完全删除。

我们考虑除去两端的 AB 之后,那么一共剩下 \(2n-1\) 个 A 和 \(n-1\) 个 B,那么根据抽屉原理显然有一个 B 的其中一边有两个 A,那么直接删除就可以归纳证明了。

会了这个之后,我们令 A 的权为 \(2\),B 的权为 \(-1\),那么一段能删除的就要求和为 \(0\)

我们考虑 dp,\(dp_{i}\) 代表前 \(i\) 个最少保留多少个字符,那么首先 \(dp_{i} = dp_{i-1}+1\),然后我们考虑把 \(i\) 连着的段删除,我们先预处理权的前缀和,我们开一个 map 记录前缀和为 \(x\),下一个字符为 A/B 的 dp 值最大是多少,直接转移即可。

给出代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
int dp[maxn], mn[maxn * 4][2], n;
string s;
void solve() {
	cin >> s, n = s.size(), s = ' ' + s;
	for (int i = 0; i <= 3 * n; i++)
		mn[i][0] = mn[i][1] = 2e9;
	int sum = 0;
	mn[n][s[1] - 'A'] = 0; 
	for (int i = 1; i <= n; i++) {
		sum += (s[i] == 'A' ? -1 : 2);
		dp[i] = dp[i - 1] + 1;
		dp[i] = min(dp[i], mn[sum + n][1 - (s[i] - 'A')]);
		if(i < n)
			mn[sum + n][s[i + 1] - 'A'] = min(mn[sum + n][s[i + 1] - 'A'], dp[i]);
		//cout << dp[i] << endl;
	}
	cout << (n - dp[n]) / 3 << endl;
}
signed main() {
	int T; cin >> T;
	while(T--)
		solve();
	return 0;
}
posted @ 2025-07-30 13:44  LUlululu1616  阅读(29)  评论(0)    收藏  举报