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

F - Yet Another Substring Reverse

題意:給出一個字符串,有一次逆置任意子串的機會,詢問一個最長子串的長度,要求子串每一個字母都不相同。

題解:問題可以看成找兩個不相交的子串,不同字母個數之和最大。因爲不同字符個數最大爲2020,考慮預處理出每一個區間的值,並維護每一個子集的最大值(也就是連續且不同字符個數最多),然後枚舉兩兩子集之和即可。

代碼

#include<bits/stdc++.h>
using namespace std;
#ifndef ONLINE_JUDGE
#define dbg(args...)                                   \
    do{                                                \
	        cout << "\033[32;1m" << #args << " -> ";   \
         err(args);                                    \
      } while(0)                                       
#else
#define dbg(...)
#endif
void err()
{
    cout << "\033[39;0m" << endl;
}
template <template <typename...> class T, typename t, typename... Args>
void err(T<t> a, Args... args)
{
    for (auto x : a) cout << x << ' ';
    err(args...);
}
template <typename T, typename... Args>
void err(T a, Args... args)
{
    cout << a << ' ';
    err(args...);
}
/****************************************************************************************************/

string s;

int dp[1 << 20];

int main() {
#ifndef ONLINE_JUDGE
    freopen("input.in","r",stdin);
#endif
    ios::sync_with_stdio(false); cin.tie(0);
	cin >> s;
	int n = s.size();
	for(int i = 0; i < n; ++i) {
		int used = 0;
		for(int j = i; j < n && j - i < 20; ++j) {
			int c = s[j] - 'a';
			if((used >> c) & 1) break;
			used |= 1 << c;
			dp[used] = j - i + 1;
		}
	}
	for(int i = 1; i < 1 << 20; ++i) {
		for(int j = 0; j < 20; ++j) {
			if(!((i >> j) & 1)) { 
				//每一個子集的最大值
				dp[i | 1 << j] = max(dp[i | 1 << j], dp[i]);
		//		dp[i] = max(dp[i], dp[i ^ (1 << j)]);
			}
		}
	}
	
	int S = 1 << 20, ans = dp[S - 1];
	for(int i = 1; i < 1 << 20; ++i) {
		int mask = i ^ (S - 1);
		ans = max(ans, dp[i] + dp[mask]);
	}
	cout << ans << '\n';
    return 0;
}

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