CF1234F 题解

题面

\(sz\) 为字符集大小。

注意到字符集大小不超过为 20,这意味着最多有 \(2^{20}\) 种由不同字母组成的字符串(两个字符串由同样的字符构成算一种,例如 abba 算同一种)。

翻转操作不太好想,但是我们注意到选择 \(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;
}
posted @ 2024-07-25 15:52  adam01  阅读(59)  评论(0)    收藏  举报