2019年杭电多校第一场 String HDU - 6586 (思维)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6586

题意:给你一个字符串,让你找出一个字典序最小的长度为k的字符串,并且需要满足以下26个限制(即a-z的数量限制)

每个字符出现的最少次数和最多次数

思路:先用26队列把每个字符出现的位置一一存起来,要求的字符串是字典序最小的  所以需要从a开始枚举到z,

字符串的长度是k,对于每一个位置来说,a-z都有可能,所以枚举每个位置,对于每个位置枚举26个字母,如果可以放进答案数组就放,放不下就break,判断的时候,用上给你的26个限制条件就可以了

对于一个位置,假设一个字符满足条件,然后判断一下 

1、这26个字符  用了的字符数+这个位置之后还没有用的字符数够不够题目要求的最低限制。  

2、这26个字符  假设只用最低限制数量的字符  判断是否比k还大,假设用上最多限制的字符,判断是否比k还小

 

 

 

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#include <vector>
#define ll long long
const int maxn = 1e6+5;
using namespace std;
char s[maxn];
int k, len, cl,flag;
int l[30],r[30];
int cnt[100010][30];
int num[30];
int ans[maxn];
int main()
{
    while(scanf("%s%d",s+1,&k)!=EOF) {
        for(int i=0;i<26;i++) {
            scanf("%d%d",&l[i],&r[i]);
            num[i]=0;
        }
        queue<int> q[30];//如果不定义在while循环内部,记得清空队列
        len = strlen(s+1);
        for(int i=1;i<=len;i++)q[s[i]-'a'].push(i);
        for(int i=0;i<26;i++)cnt[len+1][i]=0;//初始化cnt数组
        for(int i=len;i>0;i--) {//cnt[i][j]记录的是i位置之后j字符的数量
            for(int j=0;j<26;j++) {
                if(s[i]=='a'+j) cnt[i][j]=cnt[i+1][j]+1;
                else cnt[i][j]=cnt[i+1][j];
            }
        }
        cl=0;//已经放好的字母的位置
        for(int pos=1;pos<=k;pos++) {
            flag=0;
            for(int i=0;i<26;i++) {
                if(num[i]>=r[i]) continue;
                while(!q[i].empty() && q[i].front()<=cl) q[i].pop();
                if(!q[i].empty()) {
                    int ok=1;
                    num[i]++;
                    for(int j=0;j<26;j++) {//判断用上当前位置字符i之后,剩下的字符是否满足最低限制
                        if(cnt[q[i].front()+1][j]+num[j]<l[j]){
                            ok=0;
                            break;
                        }
                    }
                    if(ok) {//满足最低限制之后,判断  如果只用上最低数量的字符是否比k大,
                            //如果都用上最大数量的字符是否比k小
                        int lit1=0,lit2=0;
                        for(int j=0;j<26;j++) lit1 += max(l[j]-num[j],0);
                        for(int j=0;j<26;j++) lit2 += min(r[j]-num[j],cnt[q[i].front()+1][j]);
                        if(lit1>k-pos || lit2 <k-pos) ok=0;
                    }
                    if(!ok) {//如果不满足,那么这个位置的字符i不能当做答案
                        num[i]--;
                    }
                    else {//如果能,则更新cl,记录答案,pos位置满足,flag=1;
                        cl=q[i].front();
                        ans[pos]=i;
                        flag=1;
                        break;
                    }
                }
            }
            if(!flag) break;//如果a-z都不能放入答案那么结束循环,输出-1;
        }
        if(flag) {
            for(int i=1;i<=k;i++)
                printf("%c",ans[i]+'a');
            printf("\n");
        }
        else printf("-1\n");
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值