题目链接:传送门
思路:
由于只能翻转一次子串,就相当于找出两个不连续的子串,把在后面的一个子串翻转过来,和第一个子串拼接。
因为题目仅要求子串中的字符不重复,所以字符的先后顺序无关,翻转的操作就相当于:
选出两个不连续的子串,且他们没有相同的字符,两个子串的长度之和就是答案的一种可能。
题目中反复强调,给出的字符串只有前20个字母[a, t],考虑到$2^{20} = 10^{6}, 2^{26} = 6*10^{7}$,显然在疯狂暗示:要用状压来做这题。
所以考虑二进制状压字符集合。
一个朴素的想法:
令集合X = {'a', 'b',..., 't'},用|X|表示集合X的大小。
预处理出集合X的所有有效子集(在原字符串中能找得到)。时间复杂度为O(n*|X|) = 2*$10^{6}$。
然后再枚举X的子集A,和集合A在X中的补集B的子集C,若C是有效的(在原字符串中能找到),则|A|+|C|就是答案的一种可能。
这样做的总时间复杂度是O($2^{|X|}* 2^{|X|}$ + n*|X|) = 1e12,显然会TLE。
题解:
实际上,我们在枚举集合B的子集C的时候,如果我们能知道集合B的有效子集的大小的最大值max{|C|},就可以用|A|+max{|C|}以O(1)的时间来更新答案了。时间复杂度可以下降到O($2^{|X|}$)
接下来考虑如何预处理出max{|C|}。
如果用f[mask]表示,以mask二进制表示的集合S的最大有效子集的大小,那么:
如果S是有效的:f[mask] = |S|
否则:f[mask] = max{f[mask^(1<<i)] | 0 < i < |X| && mask&(1<<i) > 0}
这里的处理是$O(|X|*2^{|X|}) = 2*10^{7}$,是可行的。
代码:$O(|X|*2^{|X|} + |X|*n)$
1 #include <bits/stdc++.h> 2 #define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0) 3 #define N 100005 4 #define M 100005 5 #define INF 0x3f3f3f3f 6 #define mk(x) (1<<x) 7 #define sz(x) ((int)x.size()) 8 #define lson(x) (x<<1) 9 #define rson(x) (x<<1|1) 10 #define mp(a,b) make_pair(a, b) 11 #define endl '\n' 12 #define lowbit(x) (x&-x) 13 14 using namespace std; 15 typedef long long ll; 16 typedef double db; 17 18 /** fast read **/ 19 template <typename T> 20 inline void read(T &x) { 21 x = 0; T fg = 1; char ch = getchar(); 22 while (!isdigit(ch)) { 23 if (ch == '-') fg = -1; 24 ch = getchar(); 25 } 26 while (isdigit(ch)) x = x*10+ch-'0', ch = getchar(); 27 x = fg * x; 28 } 29 template <typename T, typename... Args> 30 inline void read(T &x, Args &... args) { read(x), read(args...); } 31 #define MAXMASK 20 32 33 int f[mk(MAXMASK)]; // f[mask] = mask's max number of different characters 34 int main() 35 { 36 string s; 37 cin >> s; int n = s.size(); 38 for (int i = 0; i < n; i++) { 39 int mask = 0; 40 for (int j = 0; j < MAXMASK && i+j < n; j++) { 41 int b = s[i+j] - 'a'; 42 if (mask & mk(b)) 43 break; 44 mask |= mk(b); 45 f[mask] = j+1; 46 } 47 } 48 for (int mask = 0; mask < mk(MAXMASK); mask++) { 49 for (int i = 0; i < MAXMASK; i++) if (mask & mk(i)){ 50 f[mask] = max(f[mask], f[mask ^ mk(i)]); 51 } 52 } 53 int ans = 0; 54 for (int mask = 0; mask < mk(MAXMASK); mask++) { 55 int mask1 = (mk(MAXMASK)-1) ^ mask; 56 ans = max(ans, f[mask] + f[mask1]); 57 } 58 cout << ans << endl; 59 60 return 0; 61 }View Code