2019牛客暑期多校训练营第五场 H-subsequence 2(拓扑)

本文深入解析H-subsequence2问题,介绍了一种基于拓扑排序的算法解决方案。通过对给定限制条件的分析,文章详细阐述了如何构建字母间的先后顺序关系,并通过拓扑排序找出隐藏字符串。涉及字符串处理、拓扑排序等关键技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

H-subsequence 2

题意

已经有一个长度为n的隐藏字符串仅有前m个小写字母构成(这m个字母不一定全部出现)
给出m*(m-1)/2个限制条件
每个限制条件有两行
第一行给出2个字母和一个len ,表示删除这个串中除了这2个字母的其他字母后串的长度为len
第二行给出这个长度为len的串

思路

我们可以发现由给出的限制条件,字母之间存在先后顺序的关系,也就是要想输出当前字母,要先把它前面的字母都输出完了才行,脑补一下就是拓扑的关系。
于是我们开始尝试建边构图。由于字符串长度有1e4,有26个字母,我们首先要给每个字母标号。
定义
a用0-9999之间的数来表示
b用10000-19999之间的数来表示
以此类推,我们就可以根据数的大小来计算这是哪个字母的第几个。
然后我们根据限制条件建边跑拓扑就可以了,如果串不存在,也就是构不成n个字符,就输出-1

#include<algorithm>
#include<vector>
#include<cstdio>
#include<cstring>
#include<queue>
#include<string>
#include<cmath>
#include<stdio.h>
using namespace std;
#define maxn 10005
#define maxm 500006
#define ll long long int
#define inf 0x3f3f3f3f
#define sc(x) scanf("%d",&x)
int n,m,p,len;
int f[30],d[maxn*50],sum[30];
char c1,c2;
char s[maxn];
vector<int>ans;
queue<int>q;
vector<int>ed[maxn*50];
bool flag=0;
void init(){
    for(int i=0;i<=26;i++)f[i]=i*10000;
}
int change(int u){ //这个函数用来把数转化为字母
    return u/10000;
}
int work(){
    int tot=0;
    for(int i=0;i<m;i++){ //枚举前i个字母
        for(int j=0;j<sum[i];j++){ //前i个字母的个数
            if(!d[j+f[i]])//如果这个字母的入度==0,可以塞进队列里面
                q.push(j+f[i]);
        }
    }
    while(!q.empty()){
        int u=q.front();q.pop();
        if(change(u)>=m)return -1;//出现了比m还大的字母,这是不可能的
        ans.push_back(change(u));
        tot++;
        for(int i=0;i<ed[u].size();i++){
            int v=ed[u][i];
            d[v]--;
            if(!d[v])q.push(v);
        }
    }
    return tot;
}
int main(){
    sc(n);sc(m);
    init();
    p=m*(m-1)/2;
    while(p--){
        scanf("%s%d",s,&len);//len是只有这两个字母的时候的长度
        c1=s[0];c2=s[1]; //先输入这个限制的两个字母
        if(len)scanf("%s",s); //有len=0的情况,是个坑点
        int num1,num2,u,v;
        num1=num2=0;//num1和byn2是记录这个字母是相同字母的第几个
        if(len>0){
            if(s[0]==c1){
                u=num1+f[c1-'a'];
                num1++;
            }else{
                u=num2+f[c2-'a'];
                num2++;
            }
            for(int i=1;i<len;i++){
                if(s[i]==c1){
                    v=num1+f[c1-'a'];
                    num1++;
                }else{
                    v=num2+f[c2-'a'];
                    num2++;
                }
                ed[u].push_back(v);
                u=v;
                d[v]++;
            }
        }
        //如果这次出现的次数和之前不一样,不符合条件,要特判
        if(!sum[c1-'a']||sum[c1-'a']==num1)sum[c1-'a']=num1;
        else flag=1;
        if(!sum[c2-'a']||sum[c2-'a']==num2)sum[c2-'a']=num2;
        else flag=1;
    }
    int res=work();
    if(res!=n||flag){
        printf("-1\n");
    }else{
        for(int i=0;i<ans.size();i++){
            printf("%c",ans[i]+'a');
        }
        printf("\n");
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值