CF1234F Yet Another Substring Reverse

Fire_Raku發表於2024-06-06

CF1234F Yet Another Substring Reverse

狀壓 dp+高維字首和

一個很顯然的發現是最長子串長度不會超過字符集。那麼如果沒有這個操作,是很簡單的,我們看看多了這個操作意味著什麼。

對於一個子串,考慮一次翻轉對它的影響。在它內部的翻轉肯定是沒有意義的;我們一定有一個操作能將任意子串與它接到一起,可以改變答案。那麼我們就可以刻畫這樣一個翻轉操作:將兩個各個字元不相同的(不相交的)子串接在一起(因為發現各個字元不相同已經保證了兩個子串不相交)。

考慮狀壓字符集,設 \(f_{s}\) 表示包含字符集 \(s\) 的子串是否存在,轉移就列舉 \(s\) 補集的子集 \(f_{t}\),合併即可。

複雜度 \(O(3^n)\),考慮最佳化。發現瓶頸在於列舉補集的子集,而我們只需要其中的最大值,所欲可以 \(O(2^nn)\) 高維字首和預處理每個集合的所有子集的最大值。

複雜度 \(O(2^nn)\)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10, S = 21;
int n, ans;
int f[1 << S], g[1 << S];
char s[N];
void solve() {
	std::cin >> s + 1;

	n = strlen(s + 1);
	for(int i = 1; i <= n; i++) {
		int sta = 0;
		for(int j = 0; i + j <= n; j++) {
			int c = s[i + j] - 'a';
			if((1 << c) & sta) break;
			sta |= (1 << c);
			f[sta] = std::max(f[sta], __builtin_popcount(sta));
		}
	}
	int lim = (1 << 20);
	for(int s = 0; s < lim; s++) g[s] = f[s];
	for(int i = 0; i <= 19; i++) {
		for(int s = 0; s < lim; s++) {
			if(s & (1 << i)) g[s] = std::max(g[s], g[s & (~(1 << i))]);
		}
	}
	
	for(int s = 0; s < lim; s++) {
		int s2 = (lim - 1) ^ s;
		if((!s || f[s]) && (!s2 || g[s2])) ans = std::max(ans, f[s] + g[s2]); 
	}
	std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	solve();

	return 0;
}

相關文章