Codeforces Round #710 (Div. 3) G. Maximize the Remaining String (字符串,枚举,序列自动机)

Codeforces Round #710 (Div. 3) G. Maximize the Remaining String  (字符串,枚举,序列自动机)
Codeforces Round #710 (Div. 3) G. Maximize the Remaining String  (字符串,枚举,序列自动机)

  • 题意:给你一个字符串,要求删去重复的字母,使得只剩下一个字母,那么最后的得到的字符串每个字母都只出现一次,问你怎么操作是的最后的字符串字典序最大.

  • 题解:我们先用后缀和处理每个字母出现的次数,然后再用序列自动机预处理一下,首先我们先得到删去重复字母后字符串的长度,然后再去枚举.

    因为要求字典序最大,所以对于每个位置,我们枚举\(['z','a']\),\(cur\)表示当前指针在\(s\)中的所在位置,即\(cur\)之前的元素我们都不要了,如果你了解序列自动机的话,这并不难想.那么对于当前位置\(cur\),我们得到序列自动机\(nx\)数组找到我们当前枚举到的字母的所在位置,因为我们想把我们当前所枚举到的字母放入答案,所以,\([cur,nx[cur][need]-1]\),这段区间的所有字母都需要删去,那么我们怎么知道能不能删呢?这是我们之前处理的后缀和的作用就体现出来了,我们可以枚举\(['a','z']\)每个字母,先看他们在\([cur,nx[cur][need]]\)中是否出现,如果出现,那么在\([nx[cur][need]+1,len(s)]\)这段区间中,它至少要出现一次,那么我们就可以将它删去,这里记个\(flag\)判断一下就好.

  • 代码:

    #include <bits/stdc++.h>
    #define ll long long
    #define fi first
    #define se second
    #define pb push_back
    #define me memset
    #define rep(a,b,c) for(int a=b;a<=c;++a)
    #define per(a,b,c) for(int a=b;a>=c;--a)
    const int N = 1e6 + 10;
    const int mod = 1e9 + 7;
    const int INF = 0x3f3f3f3f;
    using namespace std;
    typedef pair<int,int> PII;
    typedef pair<ll,ll> PLL;
    ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;}
    ll lcm(ll a,ll b) {return a/gcd(a,b)*b;}
    
    int suf[N][26];
    int nx[N][26];
    int cnt[N],used[N];
    
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
    	int _;
    	cin>>_;
    	while(_--){
    		string s;
    		cin>>s;
    		int len=(int)s.size();
    		s=" "+s;
    		string ans="";
    
    		rep(i,0,25){
    			cnt[i]=used[i]=0;
    			suf[len+1][i]=0;
    			nx[len+1][i]=0;
    		}
    
    		per(i,len,1){
    			cnt[s[i]-'a']++;
    			rep(j,0,25) suf[i][j]=suf[i+1][j];
    			suf[i][s[i]-'a']++;
    		}
    		per(i,len,1){
    			rep(j,0,25) nx[i][j]=nx[i+1][j];
    			nx[i][s[i]-'a']=i;
    		}
    		int len_=0;
    		rep(i,0,25) len_+=(cnt[i]!=0);
    
    		int cur=1;
    		rep(i,1,len_){
    			per(need,25,0){
    				if(!used[need] && nx[cur][need]){
    					int nxt=nx[cur][need];
    					int flag=1;
    					rep(abd,0,25){
    						if(need==abd) continue;
    						if(!used[abd] && suf[cur][abd]-suf[nxt+1][abd]){
    							flag&=(suf[nxt+1][abd]>0);
    						}
    					}
    					if(flag){
    						used[need]=1;
    						ans.pb(need+'a');
    						cur=nxt+1;
    						break;
    					}
    				}
    			}
    		}
    
    		cout<<ans<<'\n';
    
    	}
    
        return 0;
    }
    
上一篇:BZOJ4650: [Noi2016]优秀的拆分(hash 调和级数)


下一篇:P5437-[XR-2]约定【拉格朗日差值,数学期望】