HDU6586 String(贪心)

HDU6586
简单贪心题目
满足要求的字典序最小的子序列
优先级1字典序 优先级2位置靠前
因为要求是子序列 很容易想到要记录前缀和后缀和等等。
h[i][j]表示位置i之后的字符(a+‘j’)的数量
pos[i]记录了字符(a+‘i’)的所有位置集合
have[i]表示当前已经有字符(‘a’+i)的数量
有了这三个 就可以开心的贪心了
贪心需要满足的两个条件有:
1.当前选择的位置i后面剩余的任意字符的数量要满足
(h[i][j]>=l[j]-have[j])
2.当前选择的位置i后面可选择的所有字符的总和大于待拼凑的长度
∑ i = 0 25 m a x ( r [ i ] − h a v e [ i ] , 0 ) > = k ′ \displaystyle\sum_{i=0}^{25}max(r[i]-have[i],0)>=k' i=025max(r[i]have[i],0)>=k

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+50;
char s[maxn],s1[maxn];
int  h[maxn][26],haved[26],k;
int l[26],r[26];
vector<int>pos[26];
int main()
{
    while (~scanf("%s%d",s,&k)) {
        memset(haved, 0, sizeof(haved));
        memset(s1,0,sizeof(s1));
        int len=(int)strlen(s);
        memset(h[len], 0, sizeof(h[len]));
        for(int i=0;i<26;i++)
            pos[i].clear();
        for(int i=0;i<26;i++)
            scanf("%d%d",&l[i],&r[i]);
        for(int i=len-1;i>=0;i--)              //记录后缀
            for(int j=0;j<26;j++)
                h[i][j]=h[i+1][j]+(s[i]=='a'+j);
        for(int i=0;i<len;i++)
            pos[s[i]-'a'].push_back(i);          //存储对应字符的所在位置
        int last=-1;
        vector<int>::iterator iter[26];
        for(int i=0;i<26;i++) iter[i]=pos[i].begin();
        for(int i=0;i<k;i++){
            bool flag1=0;
            if(strlen(s1)==k)
                break;
            for(int j=0;j<26;j++)
            {
                if(haved[j]==r[j]) continue;
                while(iter[j]!=pos[j].end()&&(*iter[j])<=last) iter[j]++;
                if(iter[j]==pos[j].end()) continue;
                haved[j]++;
                bool flag2=0;
                int p=*iter[j],sum=0;
                for(int m=0;m<26;m++)
                {
                    if(h[p+1][m]+haved[m]<l[m])
                    {
                        flag2=1;
                    }
                }
                int summ=0;
                for(int m=0;m<26;m++)
                {
                    summ+=max(0,l[m]-haved[m]);
                }
                if(summ>=k-i)
                    flag2=1;
                summ=0;
                for(int m=0;m<26;m++)
                {
                    summ+=min(h[p+1][m],r[m]-haved[m]);
                }
                if(summ<k-i-1)
                    flag2=1;
                if(flag2==1)
                    haved[j]--;
                else
                {
                    s1[i]='a'+j;
                    flag1=1;
                    last=p;
                    break;
                }
            }
            if(flag1==0)
            {
                printf("-1\n");
                goto end;
            }
        }
        s1[k]=0;
        printf("%s\n",s1);
    end:;
    }
    return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值