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] = 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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM