【codeforces 590div3】F.Yet Another Substring Reverse

題意:

現在有一個字符串 ss1s1061 \leq |s| \leq 10^6),現在選擇一個區間[l,r][l,r],反轉一次或者不反轉。若某區間的字母各不相同,則該區間爲完美區間。你要做的就是執行完操作後使完美區間的長度最大。(整個字符串的字母種類數不超過2020

題解:

這個問題可以轉換爲尋找兩個不相交的完美區間使得他們的長度和最大。

首先枚舉所有的完美區間,複雜度爲O(nsiz)O(n \cdot siz),sizsiz爲字符種類數。

對於每個完美區間,用二進制形式來表示,若第ii個字母存在,則第ii位爲11,如abcfabcf即可表示爲:

11100100000000000000 11100100000000000000

接下來令dpmaskdp_{mask}代表maskmask所有子集的長度的最大值。這個過程可以採用二進制枚舉子集的方式進行dpdp。複雜度O(2sizsiz)O(2^{siz} \cdot {siz})

接下來其實就是找兩個不相交的狀態mask1mask1mask2mask2使得dpmask1+dpmask2dp_{mask1}+dp_{mask2}最大化,枚舉mask1mask1,mask2mask2其實就是mask1mask1按位取反。枚舉更新答案即可。

代碼:

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章