Codeforces Round #590 (Div. 3) F. Yet Another Substring Reverse(狀壓dp)

題目鏈接

這題真沒想到狀壓dp可以寫,看了別人代碼纔看會的。

題意:給你一個只包含前20個英文字母的串。你可以執行一次操作,將這個串的任意子串翻轉一次。讓你求一個最長的沒有重複字母的字串。

題解:首先看到只有二十個英文字母,無腦上狀壓dp。可以將這個串的任意子串翻轉一次,實際上就是可以讓兩個去重的子字符串合併在一起。那麼這個題就變成了枚舉子集,表示我們必須選取這一段,然後爲了使答案最優,所以去找他的補集的最大子集。那麼我們是不是可以先預處理出這個串所有的無重複字符的子串,並用二進制表示其狀態。再dp求出每個補集的最大子集,再o(n)求最大答案就行了。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
char s[N];
int vis[21], sta[1 << 20], mx[N];
int main()
{
	scanf("%s", s);
	int n = strlen(s);
	
	for(int i = 0; i < n; ++i){//預處理出所以無重複字母子串 
		memset(vis, 0 ,sizeof vis);
		int cnt = 0, tmp = 0;
		for(int j = i; j < n; ++j){//最多二十位 
			if(vis[s[j] - 'a']) break;
			else {
				tmp |= (1 << (s[j] - 'a'));
				vis[s[j] - 'a'] = 1;
				sta[tmp] = ++cnt;
			}
		}
	}
	
	int m = (1 << 20) - 1;
	
	for(int i = 0; i <= m; ++i)
	for(int j = 0; j < 20; ++j)
	if(i & (1 <<j))
	sta[i] = max(sta[i], sta[i ^ (1 << j)]);//枚舉這個集合的最大子集 
	
	int ans = 0;
	for(int i = 1; i <= m; ++i){
		int tmp = m ^  i;
		ans = max(ans, sta[i] + sta[tmp]);//子集 + 補集 
	} 
	printf("%d\n", ans);
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章