Codeforces1213 F. Unstable String Sort (缩点+拓扑排序)

题意:

给定长度为n的两个排列p和q,和一个整数k
要求构造一个长度为n的字符串s,其中至少有k个不同的字母(但是不能超过小写字母的26个)
这个字符串需要满足对于任意的i,s[p[i]]<=s[p[i+1]],s[q[i]]<=s[q[i+1]]
如果无解输出NO,否则输出YES和构造出的串s

数据范围:n<=2e5,k<=26

解法:

因为题目中的不等式用的是小于等于号,那么s串所有字符相等显然是一种方案
但是题目还要求至少有k个不同的字母,因此所有字符相等的方案可能不行了,需要考虑如何增加字符种类

p[i]对p[i+1]建立有向边,q[i]对q[i+1]建立有向边
这里有向边的意义是小于等于,那么如果出现环了,泽这个环内的字符必须相同,将环缩点
然后图就变为了一个有向无环图,这时候假如图中点a指向点b,表明点a位置的字符<=点b位置的字符,
因为我们要考虑增加字符种类,不妨令a位置的字符和b位置的字符不同,即小于号,让字符种类最大
可以用拓扑排序给每个点赋一个字符值,如果总字符值的种数小于k,说明无解
否则有解,不够字符数量不能超过小写字母的26个,解决方法是将大于26的改成26即可,因为是小于等于号所以没影响。

code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+5;
vector<int>g[N];
vector<int>gg[N];
int p[N],q[N];
int n,k;
//
int low[N],dfn[N],idx;
int belong[N],sum;
stack<int>stk;
int ins[N];
int in[N];
int cc[N];
//
void dfs(int x){
    low[x]=dfn[x]=++idx;
    stk.push(x);
    ins[x]=1;
    for(int v:g[x]){
        if(!dfn[v]){
            dfs(v);
            low[x]=min(low[x],low[v]);
        }else if(ins[v]){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(low[x]==dfn[x]){
        sum++;
        while(1){
            int t=stk.top();
            stk.pop();
            ins[t]=0;
            belong[t]=sum;
            if(t==x)break;
        }
    }
}
signed main(){
    cin>>n>>k;
    for(int i=1;i<=n;i++)cin>>p[i];
    for(int i=1;i<=n;i++)cin>>q[i];
    for(int i=1;i<n;i++){
        g[p[i]].push_back(p[i+1]);
    }
    for(int i=1;i<n;i++){
        g[q[i]].push_back(q[i+1]);
    }
    //
    for(int i=1;i<=n;i++){
        if(!dfn[i])dfs(i);
    }
    for(int x=1;x<=n;x++){
        for(int v:g[x]){
            if(belong[x]!=belong[v]){
                in[belong[v]]++;
                gg[belong[x]].push_back(belong[v]);
            }
        }
    }
    //
    queue<int>q;
    int now=0;
    for(int i=1;i<=sum;i++){
        if(in[i]==0){
            q.push(i);
            cc[i]=++now;
        }
    }
    while(!q.empty()){
        int x=q.front();q.pop();
        for(int v:gg[x]){
            in[v]--;
            if(!in[v]){
                q.push(v);
                cc[v]=++now;
                if(cc[v]>26)cc[v]=26;//超过26的改成26
            }
        }
    }
    if(now<k){
        puts("NO");
    }else{
        puts("YES");
        for(int i=1;i<=n;i++){
            cout<<(char)(cc[belong[i]]+'a'-1);
        }
    }
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值