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