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);
}

 

 

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