Codeforces1234F. Yet Another Substring Reverse(状压dp)

Posted lubixiaosi-zhaocao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces1234F. Yet Another Substring Reverse(状压dp)相关的知识,希望对你有一定的参考价值。

题目链接:传送门

思路:

由于只能翻转一次子串,就相当于找出两个不连续的子串,把在后面的一个子串翻转过来,和第一个子串拼接。

因为题目仅要求子串中的字符不重复,所以字符的先后顺序无关,翻转的操作就相当于:

选出两个不连续的子串,且他们没有相同的字符,两个子串的长度之和就是答案的一种可能。

题目中反复强调,给出的字符串只有前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] = maxf[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

 

以上是关于Codeforces1234F. Yet Another Substring Reverse(状压dp)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces 868F. Yet Another Minimization Problem

CodeForces868F. Yet Another Minimization Problem

Codeforces 868F. Yet Another Minimization Problem决策单调性优化DP分治莫队

Codeforces Round #438 by Sberbank and Barcelona Bootcamp (Div. 1 + Div. 2 combined) F. Yet Another M

CF1234F Yet Another Substring Reverse (状压dp)

Educational Codeforces Round 88 (Rated for Div. 2) D. Yet Another Yet Another Task