http://m3.codeforces.com/contest/1296/problem/E2
题目是说给你一串有小写字母构成的序列。
现在你可以给每个字符上色,注意是同一个字母可以有多种颜色,但是刚才1说的是字符,也i就是每一个位置就只有一种颜色,现在,你有一个能力就是将不同颜色的字符可以进行交换,问你需要多少种颜色才可以将原序列变成有序列;
输出颜色种数而且还要输出方案;
思路:
我们可以发现一个字符必须和他前面的比他大的字符进行交换,我们把这些比他的字符统称集合s,那么就得我们这个字符颜色得和他们不一样,那么颜色肯定选择集合里最小的从未出现的,就是mex懂吧。例如集合里的颜色有2 ,4 ,5,那此时的颜色肯定是1。那么我们维护每个字母的并查集,也就是从头遍历字符串,当前遇到字符串为id,我们就进入该字符的所属并查集,然后开始找到他的父亲,那么这个新字符就为父亲的颜色(x)+1;然后我们接下来就对小于id的字符的颜色(x,也就是和左边那个x同一个颜色)进行归并父亲到当前的x+1,这里的意思大白话来讲就是,后面出现比id小的为了和id不同只能选不同颜色,但是id的颜色本身就是按这个从1,2,3,4,5慢慢一步一步选过来的规则,所以后面比id小的字符只能比id的颜色+1;
#include<bits/stdc++.h> #define MAXN 200005 using namespace std; int n,vis[26][MAXN],pre[26][MAXN],ans[MAXN]; char s[MAXN]; inline int find(int id,int x) { return x == pre[id][x] ? x : pre[id][x] = find(id,pre[id][x]); } int main() { scanf("%d%s",&n,s+1); for(int i = 0;i < 26;++i) { for(int j = 0;j <= n;++j) pre[i][j] = j; } int mx = 1; for(int i = 1;i <= n;++i) { int id = s[i] - 'a'; ans[i] = find(id,0)+1; mx = max(mx,ans[i]); for(int j = 0;j < id;++j) { if(!vis[j][ans[i]]) { vis[j][ans[i]] = 1; pre[j][ans[i]-1] = ans[i]; } } } printf("%d\n",mx); for(int i = 1;i <= n;++i) printf("%d ",ans[i]); return 0; }