CF1234F 题解
令 \(sz\) 为字符集大小。
注意到字符集大小不超过为 20,这意味着最多有 \(2^{20}\) 种由不同字母组成的字符串(两个字符串由同样的字符构成算一种,例如 ab 和 ba 算同一种)。
翻转操作不太好想,但是我们注意到选择 \(S_{[l1,r1]}\) 和 \(S_{[l2,r2]}\) 相当于翻转 \(S_{[r1+1,r2]}\) 再选择从 \(S_{l1}\) 开始的子串。
因而把子串翻转意味着在 \(S\) 中选两个字符串,使得拼起来后各个字符依然都不相同,并且长度最长。
于是可以直接统计有多少种由不同字母组成的字符串。因为串长不超过 \(sz\),于是复杂度为 \(O(sz\times N)\)。
现在的问题是怎么找到最长的两个满足要求的字符串。
注意到两个字符串没有相同的字符,所以可以 \(O(2^{sz})\) 二进制枚举把每个字符分配给那一边。设被拆开成两个字符集为 \(A,B\)。
注意到字符串不一定能用完被分配的字符,所以两个串所构成的两个字符集分别是 \(A,B\) 的子集。
这时只需要用状压的思想,把集合变成二进制数,用高维前缀和(SOS dp)枚举每一位转移,就可以算出对于每个集合,子集答案的最大值,复杂度为 \(O(sz\times 2^{sz})\)。
所以总复杂度为 \(O(sz\times 2^{sz} + sz\times N)\),也就是 \(20\times 2^{20} + 20N\)。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1 << 20;
int f[N];
string s;
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);
cin >> s;
f[0] = 0;
for(int r = 0; r < s.size(); r ++)
for(int k = r, now = 0; k >= 0; k --) // 暴力统计子串种类
{
if(now & (1 << s[k] - 'a')) break;
now |= (1 << s[k] - 'a');
f[now] = __builtin_popcount(now);
}
for(int i = 0; i < 20; i ++) // 枚举每一位
for(int j = 0; j < (1 << 20); j ++)
f[j | (1 << i)] = max(f[j | (1 << i)], f[j]);
int ans = 0;
for(int i = 0; i < (1 << 20); i ++)
ans = max(ans, f[i] + f[((1 << 20) - 1) ^ i]);
cout << ans;
return 0;
}

浙公网安备 33010602011771号