题意:给出一个字符串,每次可以删除一个 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;
}