題意:
現在有一個字符串 (),現在選擇一個區間,反轉一次或者不反轉。若某區間的字母各不相同,則該區間爲完美區間。你要做的就是執行完操作後使完美區間的長度最大。(整個字符串的字母種類數不超過)
題解:
這個問題可以轉換爲尋找兩個不相交的完美區間使得他們的長度和最大。
首先枚舉所有的完美區間,複雜度爲,爲字符種類數。
對於每個完美區間,用二進制形式來表示,若第個字母存在,則第位爲,如即可表示爲:
接下來令代表所有子集的長度的最大值。這個過程可以採用二進制枚舉子集的方式進行。複雜度
接下來其實就是找兩個不相交的狀態和使得最大化,枚舉,其實就是按位取反。枚舉更新答案即可。
代碼:
#include<bits/stdc++.h>
using namespace std;
int dp[1<<21];//dp[mask]表示mask所有子狀態的最大值
int cal(int x){
int ans=0;
while(x){
ans+=x%2;
x/=2;
}
return ans;
}
int main(){
string s;
cin>>s;
int l=(int)s.length();
memset(dp,0,sizeof(dp));
for(int i=0;i<l;i++){//枚舉s[i:j]滿足s[i:j]各個字母不相同
bool vis[20];
memset(vis,0,sizeof(vis));
int mask=0;
for(int j=0;i+j<l;j++){
if(vis[s[i+j]-'a']) break;
vis[s[i+j]-'a']=1;
mask|=(1<<(s[i+j]-'a'));
dp[mask]=cal(mask);
//cout<<mask<<' '<<dp[mask]<<endl;
}
}
for(int mask=0;mask<(1<<20);mask++){
for(int j=0;j<20;j++){
if((mask>>j)&1){
dp[mask]=max(dp[mask],dp[mask^(1<<j)]);//枚舉子集更新最大值
//cout<<mask<<' '<<dp[mask]<<endl;
}
}
}
int ans=0;
for(int mask=0;mask<(1<<20);mask++){
if(dp[mask]==cal(mask)){
int now=~mask&((1<<20)-1);
//cout<<mask<<' '<<now<<endl;
ans=max(ans,dp[mask]+dp[now]);
}
}
cout<<ans<<endl;
return 0;
}