题解:AGC027E ABBreviate
题解:AGC027E ABBreviate
题目描述
给定一个只含小写字母 a,b 的字符串 s,每次你可以执行以下两种操作:
- 选取 s 中连续的两个字符 aa,把它们删去,替换成一个字符 b。
- 选取 s 中连续的两个字符 bb,把它们删去,替换成一个字符 a。
请你求出执行若干次操作后,能够得到的本质不同的字符串有多少个,答案对 \((10^9+7)\) 取模。
\(1≤|s|≤10^5\)。
题解
将 a 改为 \(+1\),b 改为 \(- 1\),则每次操作后整个字符串的和 \(\bmod 3\) 不会改变。所以可以得出结论:如果初始的和 \(\bmod 3=0\),则这个字符串一定不能操作为单个字符。那剩下的都可以吗?abababa 这样的字符串显然不能操作为一个字符,我们接下来说明除此之外总和 \(\bmod 3=\pm 1\)、有连续的两个相同字符的字符串都可以操作到只剩一个字符:如果是 aaaa 或者 bbbb 这样只有一种字符的字符串,操作前两个字符;否则从字符串中选出 abb 或 bba 或 aab 或 baa 子串(显然能找到),操作连续的两个相同字符,操作完了以后字符串还是有连续的两个相同字符,可以继续操作。
为了做这道题,我们可以发现得到的字符串(设为 \(t\))的每个字符都是由 \(s\) 中一段区间操作得来的,那就好办了,我们构造一下 \(s\) 构造 \(t\) 的唯一方案就能 dp 了。考虑遍历 \(t\) 的每个字符,从 \(s\) 里贪心的选最短的前缀使它构造出这个字符,然后删掉。可以理解到这样是最好的。最后剩下一段后缀,如果这段后缀的总和不是 \(0\) 就寄了,然后去看看这个后缀放到哪里。如果划分出的最后一段有不只一个字符,或者最后一段的字符和剩下的后缀一样,或者剩下的后缀里面有连续的两个相同字符,就赢了。这时就只剩下 ...[a]baba 的恶劣情况,考虑改成 ...abab[a] 把这个后缀扔到前面去尝试结合。如果递归到底发现结合不了,就发现 \(s\) 为 abababababa 这种应该直接特判输出 \(1\) 的情况,这样就结束了。
dp 就可以考虑令 \(nxt_i\) 表示最小的 \(j\) 使得 \(s[i,j]\) 可以制造出 \(s[i]\oplus 1\),然后就使 \(f_i\to f_{i+1}\) 或 \(f_{nxt[i+1]}\) 就好了。时间复杂度 \(O(n)\)。
代码
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
template <unsigned umod>
struct modint {/*{{{*/
static constexpr int mod = umod;
unsigned v;
modint() = default;
template <class T, enable_if_t<is_integral<T>::value, int> = 0>
modint(const T& y) : v((unsigned)(y % mod + (is_signed<T>() && y < 0 ? mod : 0))) {}
modint& operator+=(const modint& rhs) { v += rhs.v; if (v >= umod) v -= umod; return *this; }
modint& operator-=(const modint& rhs) { v -= rhs.v; if (v >= umod) v += umod; return *this; }
modint& operator*=(const modint& rhs) { v = (unsigned)(1ull * v * rhs.v % umod); return *this; }
modint& operator/=(const modint& rhs) { assert(rhs.v); return *this *= qpow(rhs, mod - 2); }
friend modint operator+(modint lhs, const modint& rhs) { return lhs += rhs; }
friend modint operator-(modint lhs, const modint& rhs) { return lhs -= rhs; }
friend modint operator*(modint lhs, const modint& rhs) { return lhs *= rhs; }
friend modint operator/(modint lhs, const modint& rhs) { return lhs /= rhs; }
template <class T> friend modint qpow(modint a, T b) {
modint r = 1;
for (assert(b >= 0); b; b >>= 1, a *= a) if (b & 1) r *= a;
return r;
}
friend int raw(const modint& self) { return self.v; }
friend ostream& operator<<(ostream& os, const modint& self) { return os << raw(self); }
explicit operator bool() const { return v != 0; }
modint operator-() const { return modint(0) - *this; }
bool operator==(const modint& rhs) const { return v == rhs.v; }
bool operator!=(const modint& rhs) const { return v != rhs.v; }
};/*}}}*/
using mint = modint<1000000007>;
constexpr int N = 1e5 + 10;
int n, pre[N], nxt[N];
char s[N];
vector<int> buc[3];
mint f[N];
int main() {
#ifndef LOCAL
cin.tie(nullptr)->sync_with_stdio(false);
#endif
cin >> s;
n = strlen(s);
memmove(s + 1, s, strlen(s));
bool flag = true;
for (int i = 1; i < n; i++) flag &= s[i] != s[i + 1];
if (flag) return cout << 1 << endl, 0;
for (int i = 1; i <= n; i++) pre[i] = pre[i - 1] + (s[i] == 'a' ? +1 : 2), buc[pre[i] % 3].push_back(i);
int p = n + 1;
for (int i = n; i >= 1; i--) {
int k = (pre[i - 1] + (s[i] == 'a' ? 2 : +1)) % 3;
while (buc[k].size() >= 2 && buc[k].end()[-2] >= p) buc[k].pop_back();
if (!buc[k].empty() && buc[k].back() >= p) nxt[i] = buc[k].back();
if (s[i] == s[i - 1]) p = i;
}
f[0] = 1;
for (int i = 0; i < n; i++) {
f[i + 1] += f[i];
if (nxt[i + 1]) f[nxt[i + 1]] += f[i], debug("nxt[%d] = %d\n", i + 1, nxt[i + 1]);
}
for (int i = 0; i <= n; i++) debug("f[%d] = %d\n", i, f[i]);
mint ans = 0;
for (int i = 1; i <= n; i++) {
if (pre[n] % 3 == pre[i] % 3) ans += f[i], debug("ans += f[%d]\n", i);
}
cout << ans << endl;
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/18898923/solution-agc027e
浙公网安备 33010602011771号