題目大意:
給定一段長度為n的字符串s
你需要給每個字符進行塗色,然后相鄰的不同色的字符可以進行交換
需要保證塗色后能通過相鄰交換把這個字符串按照字典序排序(a~z)
你可以使用無限種顏色,但是要保證用到的顏色種類最少
從1開始對顏色進行編號,先輸出最少使用的顏色種類,再給出塗色方案
解題思路 1:
可以引入一個 r 數組,開26個空間代表26種字母
這個數組 r[i] 的值代表 第 i 個字母在前面塗的顏色最大編號是多少,0表示沒出現過
遍歷這個字符串,再從當前字母后一個位置開始往后再遍歷這個數組,即找比當前字母要大的所有字母中編號最大的那個字母的編號
因為相同顏色彼此不能交換,所以同一種顏色組成的序列絕對是非嚴格遞增的
基於這個結論,順序遍歷字符串時,假設在塗顏色編號為1的字符
如果遇到一個字符比前面最大的塗了編號1顏色的字符小,為了不破壞非嚴格遞增的條件,就必須把他塗作編號2
如果遇到一個字符比前面最大的塗了編號1和編號2顏色的字符都小,為了不破壞非嚴格遞增的條件,就必須把他塗作編號3
以此類推可以得到,假設遍歷到某個字符時,前面比這個字符大的字符中塗過的顏色編號最大為 d
那么 1~d 的所有顏色都已經出現在比這個字符大的字符上
又因為要讓這個字符往前移動到它應該在的位置,那么它的顏色就一定要不同於所有的比它大的字符出現過的所有顏色
所以就可以得出結論,每次找比當前字母大的所有字母中塗色方案最大的編號+1,即為當前字母塗色方案
代碼如下
#include<bits/stdc++.h> using namespace std; void solve(){ int n,i,j,d,r[26]={0},ans2=1; string s; cin>>n>>s; vector<int> 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; ans.emplace_back(d); ans2=max(ans2,d); } cout<<ans2<<'\n'<<ans[0]; for(i=1;i<n;i++) cout<<' '<<ans[i]; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); solve(); return 0; }
解題思路 2:
其實根據easy版本就能猜到
塗0顏色的是非嚴格遞增的
塗1顏色的也是非嚴格遞增的
其實這也是上面的結論
因為相同顏色彼此不能交換,所以必定是已經有順序的
那么就可以得到這一種想法,編號從1開始,從頭到尾遍歷出一條非嚴格遞增的標記為顏色1,如果標記出來的數量和小於n(即還有一些字符沒有被標記),那么就加一種顏色,重新再來一遍,以此循環,直到所有字符都被標記上了顏色
#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,0); while(done<n){ mx='a'; cur++; for(i=0;i<n;i++){ if(!ans[i]&&s[i]>=mx){ mx=s[i]; done++; ans[i]=cur; } } } cout<<cur<<'\n'<<ans[0]; for(i=1;i<n;i++) cout<<' '<<ans[i]; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); solve(); return 0; }