Codeforces 1296E2

题目大意:

给定一段长度为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<<'
'<<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<<'
'<<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;
}
原文地址:https://www.cnblogs.com/stelayuri/p/12262365.html