杭电2019多校第一场 HDU——6586 String(队列+贪心)

Tom has a string containing only lowercase letters. He wants to choose a subsequence of the string whose length is k and lexicographical order is the smallest. It's simple and he solved it with ease.
But Jerry, who likes to play with Tom, tells him that if he is able to find a lexicographically smallest subsequence satisfying following 26 constraints, he will not cause Tom trouble any more.
The constraints are: the number of occurrences of the ith letter from a to z (indexed from 1 to 26) must in [Li,Ri]

.
Tom gets dizzy, so he asks you for help.

Input

The input contains multiple test cases. Process until the end of file.
Each test case starts with a single line containing a string S(|S|≤105)

and an integer k(1≤k≤|S|).
Then 26 lines follow, each line two numbers Li,Ri(0≤Li≤Ri≤|S|).
It's guaranteed that S consists of only lowercase letters, and ∑|S|≤3×105

.

Output

Output the answer string.
If it doesn't exist, output −1

.

Sample Input

aaabbb 3
0 3
2 3
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0
0 0

Sample Output

abb

题意:给你一个字符串和k,接下来26行给出每个字母的个数限制(l[i],r[i])。让你构造一个字典序最小的长度为k的满足限制条件的一个子序列串(可以不连续,但要符合顺序),没有则输出-1。

题解:将26个字母各自放入队列中,并记下每个位置各字母的后缀和。往k个位置贪心的放字母,先从a开始尝试。首先字母的数量不能大于最大限制,然后位置要大于上一个放的字母。要放这个字母还要满足两个条件。

1.对于26个字母,其后的个数加上当前选的个数要小于l[i]。

2.对于26个字母,把其后的个数取最小之后的和不能大于k,把其后的个数取最大之后的和不能小于k。

就这样贪心的放,再检查是否符合条件。

注意:写的时候要特别注意下标

上代码:

#include <iostream>
#include <cstdio>
#include <queue>
#include <cstring>
using namespace std;
const int MAX = 30;
int l[MAX],r[MAX],num[MAX];
int sum[100010][MAX];
char s[100010],ans[100010];
int len,k;
void init(){
    for (int i = 0; i < 26;i++){
        sum[len+1][i]=0;
        num[i]=0;
    } 
}
int main(){
    while(~scanf("%s%d",s+1,&k)){
        queue<int> q[30];
        len=strlen(s+1);
        init();
        for (int i = 0; i < 26;i++) cin >> l[i] >> r[i];
		for (int i = 1; i <= len;i++) q[s[i]-'a'].push(i);//注意下标
        for (int i = len; i >= 1;i--){
            for (int j = 0; j < 26;j++){
                sum[i][j]=sum[i+1][j]+(s[i]==('a'+j));
            }
        }
        int now=0;
        bool flag=false;
        int cntt=0;
        for (int i = 1; i <= k;i++){
            for (int j = 0; j < 26;j++){
                if(num[j]==r[j]) continue;//不能大于最大限制
                while(!q[j].empty()&&now>=q[j].front()) q[j].pop();
                if(!q[j].empty()){
                    bool f=false;
                    int cnt=q[j].front();
                    num[j]++;//先放上这个字母,再判断行不行
                    for (int ii = 0; ii < 26;ii++){//判断满足条件一
                        if(num[ii]+sum[cnt+1][ii]<l[ii]){
                            num[j]--;
                            f=true;
                            break;
                        }
                    }
                    if(f) continue;
                    int minn,maxx;
                    minn=maxx=0;
                    for (int ii = 0; ii < 26;ii++){//判断满足条件二
                        minn+=max(l[ii]-num[ii],0);
                        maxx+=min(r[ii]-num[ii],sum[cnt+1][ii]);
                    }
                    if(minn+i<=k&&maxx+i>=k){//判断满足条件二
                        ans[cntt++]=('a'+j);
                        now=q[j].front();
                        break;
                    }
                    else num[j]--;
                }
            }
            if(cntt!=i) break;
        }
        if(cntt==k){
        	ans[k]='\0';//注意这里的'\0'!!!
        	printf("%s\n",ans);
		}
        else cout << -1 << endl;
    }
    return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

心脏dance

如果解决了您的疑惑,谢谢打赏呦

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值