题目大意:
给定一段长度为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; }