CF1550E Stringforces (二分+状压 dp)
二分+状压 dp
可以看到最大值最小的信息,考虑二分最小值,判断是否存在方案满足条件。
思考答案的最终状态如果只看每个字母最长连续子串的部分形如 aaa...ccc...bbbb...ddd
。每个时刻会有一些字符满足条件,所以考虑状压字符集,朴素设 \(f_{i,s}\) 表示考虑到第 \(i\) 个,是否存在满足条件的字符集状态为 \(s\) 的方案。但显然是无法开出这样的数组的。所以我们考虑将 \(i\) 这一位放到 \(f_{i,s}\) 里,也就是记 \(f_{s}\) 表示满足条件的字符集状态为 \(s\) 的最短长度。
转移枚举下一次要满足的字符,那么就要用满足这个字符的最小右端点转移。可以考虑递推预处理出从每 \(i\) 位置开始,满足字符 \(j\) 的最小右端点 \(g_{i,j}\),转移有 \(g_{i,j}=g_{i+1,j}\),还有从当前位开始的连续子串是否可以,这个可以 st 表预处理出区间每个字符代表的二进制的或。
转移完后判断 \(f_{lim}\le n\) 即可,那么就完了。
复杂度 \(O(2^v\log n)\)。
#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 = 2e5 + 10, P = 18;
int n, k, ans, lim;
char s[N];
int f[1 << P], a[N][P], lg[N], nxt[N][P];
int qry(int l, int r) {
int p = lg[r - l + 1];
return a[l][p] | a[r - (1 << p) + 1][p];
}
bool check(int x) {
if(!x) return 1;
for(int i = 0; i < lim; i++) f[i] = n + 1;
for(int i = 1; i <= n + 2; i++) for(int j = 1; j <= k; j++) nxt[i][j] = n + 1;
for(int i = n; i >= 1; i--) {
for(int j = 1; j <= k; j++) {
nxt[i][j] = nxt[i + 1][j];
if(i + x - 1 <= n && (qry(i, i + x - 1) == (1 << j) || !qry(i, i + x - 1))) nxt[i][j] = i + x - 1;
}
}
f[0] = 0;
for(int s = 0; s < lim; s++) {
for(int j = 0; j < k; j++) {
if(!(s & (1 << j))) {
f[s | (1 << j)] = std::min(f[s | (1 << j)], nxt[f[s] + 1][j + 1]);
}
}
}
return f[lim - 1] <= n;
}
void solve() {
std::cin >> n >> k;
lim = (1 << k);
std::cin >> s + 1;
for(int i = 1; i <= n; i++) {
if(s[i] != '?') {
int c = s[i] - 'a' + 1;
a[i][0] = (1 << c);
}
}
for(int i = 2; i <= n; i++) lg[i] = lg[i >> 1] + 1;
for(int j = 1; j <= 18; j++) {
for(int i = 1; i + (1 << j) - 1 <= n; i++) {
a[i][j] = a[i][j - 1] | a[i + (1 << (j - 1))][j - 1];
}
}
int l = 0, r = n;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) l = mid + 1, ans = mid;
else r = mid - 1;
}
std::cout << ans << "\n";
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
solve();
return 0;
}