题意:
给一个长度为n的字符串$(nleq 1e6)$,字母从a-t(20个字母),可以执行一次操作,将字符串的某一段前后翻转,操作后,子串中所有字母都不相同的最长的子串的长度为多少。
解题思路:
一共20个字母而不是26个字母,几乎是把状压两个字写在脸上了。不难看出,对任意两个不相邻的子串,可以通过翻转操作将其拼接起来,因此任务变成了寻找两个没有相同字母的子串,使其长度和最大。
对于$dp[i]$,表示所有为0的字母项不存在情况下最长的子串长度。因此,可以得到转移方程,$dp[i|(1<<j)]=max(dp[i],dp[i|(1<<j)])$,最后,只需要枚举i,加上$dp[(1<<20)-1-i]$取max即可。
最后贴上代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef long double ld; typedef unsigned long long ull; typedef pair <int,int> pii; #define rep(i,x,y) for(int i=(x);i<(y);i++) #define rept(i,x,y) for(int i=(x);i<=(y);i++) #define per(i,x,y) for(int i=x;i>=y;i--) #define pb push_back #define mp make_pair #define fi first #define se second #define de(x) cout<< #x<<" = "<<x<<endl #define dd(x) cout<< #x<<" = "<<x<<" " #define mes(a,b) memset(a,b,sizeof a) const int inf= 0x3f3f3f3f; string s; int dp[1<<20]; int main() { ios::sync_with_stdio(false); cin.tie(0); mes(dp,0); cin>>s; int len=s.size(); rep(i,0,len) { int val=0; rep(j,i,len) { if( val&(1<<(s[j]-'a')) ) { //dd(i);de(j);//dd(val);de((1<<(s[j]-'a'))); break; } val|=(1<<(s[j]-'a')); dp[val]=j-i+1; } } rep(i,0,(1<<20)) rep(j,0,20) { if(i&(1<<j)) continue; dp[i|(1<<j)]=max(dp[i],dp[i|(1<<j)]); } int ans=0; rep(i,0,(1<<20)) { ans=max(ans,dp[i]+dp[((1<<20)-1)^i]); } cout<<ans<<" "; return 0; }