題目大意:
給定一段長度為n的字符串s
你需要給每個字符進行塗色,然后相鄰的不同色的字符可以進行交換
需要保證塗色后能通過相鄰交換把這個字符串按照字典序排序(a~z)
你只有兩種顏色可以用來塗
問是否存在這么一種塗色方案滿足題意
存在,輸出YES,再用01表示兩種不同的顏色,把塗色方案輸出(如果有多種,輸出任意一種)
不存在,輸出NO
解題思路 1:
因為只有兩種顏色可以用來塗
相同顏色彼此不能交換
所以同一種顏色組成的序列絕對是非嚴格遞增的
那么就用mx記錄一種顏色代表的非嚴格遞增的序列到某個位置時的最大字符,pmx記錄另一種顏色代表的到某個位置時的最大字符
只要在找第一種顏色時出現了一個比最大值小的,就把他推給第二種顏色
如果也比第二種顏色最大值小,說明至少得三種顏色才能滿足,此情況直接輸出NO
#include<bits/stdc++.h> using namespace std; void solve(){ int n,i; string s,ans=""; cin>>n>>s; char mx='a',pmx='a'; for(i=0;i<n;i++){ if(s[i]>=mx){ mx=max(mx,s[i]); ans+="0"; } else{ if(pmx<=s[i]){ ans+="1"; pmx=max(pmx,s[i]); } else{ cout<<"NO\n"; return; } } } cout<<"YES\n"<<ans; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); solve(); return 0; }
解題思路 2:
※該思路可直接用於題 E2 hard 版本
可以引入一個 r 數組,開26個空間代表26種字母
這個數組 r[i] 的值代表 第 i 個字母在前面塗的顏色最大編號是多少,0表示沒出現過
遍歷這個字符串,每遍歷到一個字母時,從當前字母后一個位置開始往后再遍歷這個數組,即找比當前字母要大的所有字母中編號最大的那個字母的編號
根據解題思路1,如果一個字符比一種顏色最大值要小,那么就把它推給下一種顏色
如果這個過程所有顏色都按照從1開始遞增的順序來,那么,這個字符的顏色編號會比前面的比他大的字符中編號最大的那個編號還要大
就可以得出結論,每次找比當前字母大的所有字母中塗色方案最大的編號+1,即為當前字母塗色方案
只要塗色方案出現大於2種,直接輸出NO
#include<bits/stdc++.h> using namespace std; void solve(){ int n,i,j,d,r[26]={0}; string s; cin>>n>>s; string ans; for(i=0;i<n;i++){ d=1; for(j=s[i]-'a'+1;j<26;j++)//從當前字母后一個位置開始往后找 d=max(d,r[j]+1); r[s[i]-'a']=d; if(d>2){ cout<<"NO"; return; } ans+=((d-1)?"1":"0"); } cout<<"YES\n"<<ans; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); solve(); return 0; }
解題思路 3:
※該思路可直接用於題 E2 hard 版本
因為相同顏色彼此不能交換,所以必定是已經有順序的
那么就可以得到這一種想法,編號從1開始,從頭到尾遍歷出一條非嚴格遞增的標記為顏色0,如果標記出來的數量和小於n(即還有一些字符沒有被標記),那么就加一種顏色,重新再來一遍,如果還是沒有全部塗起來,說明顏色至少要3種,直接返回NO
#include<bits/stdc++.h> using namespace std; void solve(){ int n,i,done=0,cur=0; char mx; string s; cin>>n>>s; vector<int> ans(n,-1); while(done<n){ mx='a'; if(cur>1){ cout<<"NO"; return; } for(i=0;i<n;i++){ if(ans[i]==-1&&s[i]>=mx){ mx=s[i]; done++; ans[i]=cur; } } cur++; } cout<<"YES\n"; for(i=0;i<n;i++) cout<<ans[i]; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); solve(); return 0; }