2019计蒜之道复赛-D-星云系统(线段树|树状数组区间最值)

时限:1000ms         空间限制:131072K

题目链接https://nanti.jisuanke.com/t/39614

VIPKID 是在线少儿英语教育平台,网络稳定是在线教育课程质量的红线,VIPKID 为此推出了全球最稳定的教育网络系统 —— “星云系统”。星云系统目前建立了覆盖全球 35 个国家的 5 条核心跨海专线,在 16 个国家的 55个城市建立了中心传输节点,具备一分钟内自由切换路由的能力,确保了全球跨洋课堂的高清音、视频通信,为流畅的课堂体验打下坚实基础。

全世界的中心传输节点和各地的网络节点组成的这个“星云系统”,何其复杂。我们现在只考虑一条支线上的网络节点,每一个网络节点比作一个字符的话,这条支线就是一个字符串。

现在给定你一个字符串 s 以及一个整数 k,请求出 s 的字典序最小的长度为 k 的子序列。

输入格式

第一行一个由小写英文字母构成的字符串 s,第二行一个正整数 k。

输出格式

一行一个字符串 ans,表示答案。

数据规模

0<k≤∣s∣≤5000000

样例输入

helloworld
5

样例输出

ellld

乍一看好像有点难,当看看样例就应该差不多知道了,我们直接求区间中最小的字符,然后将这个区间往后移动就好了。。。。当时AC最多的一题。我们设置head=1,tail=len(s)-k+1,然后在这里面找到最小字符输出,接着head接在这个最小字符的后面,tail++就可以继续找了,比较简单就不多说了。但注意的是这里的空间有点限制,所以开一颗线段树就好了,再开一颗会MLE,所以最好用树状数组。

当然官方题解是单调栈。。。这就非常快了:

设串长为 n,则只需删掉n-k 个字符。用一个单调栈维护,依次将字符串的每个字符插入,如果当前删掉的字符不足 n-k个并且栈顶 元素 插入的元素,那么删掉栈顶,直至删掉的字符达到n-k 或者满足单调栈的性质。 最后取栈里前k 个字符输出即可。

以下是AC代码:

线段树(常数有点大,900ms+):

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define ls rt<<1
#define rs rt<<1|1

const int mac=5e6+10;

char s[mac];
int tree[mac<<2];

void build(int l,int r,int rt)
{
    tree[rt]=999;
    if (l==r) {
        tree[rt]=s[l]-'a';
        return;
    }
    int mid=(l+r)>>1;
    build(lson);build(rson);
    tree[rt]=min(tree[ls],tree[rs]);
}

int query(int l,int r,int rt,int L,int R)
{
    int ans=999;
    if (l>=L && r<=R) return tree[rt];
    int mid=(l+r)>>1;
    if (mid>=L) ans=min(ans,query(lson,L,R));
    if (mid<R) ans=min(ans,query(rson,L,R));
    return ans;
}

int main()
{
    int k;
    scanf ("%s",s+1);
    scanf ("%d",&k);
    int len=strlen(s+1);
    build(1,len,1);
    int head=1,tail=len-k+1,sum=0;
    for (int i=1; i<=len; i++){
		int p=query(1,len,1,head,tail);
        printf ("%c",p+'a');
        int j;
        for (j=head; j<=tail; j++) if (s[j]=='a'+p) break;
        head=j+1;tail++;
        if (tail>len) tail=len;
        sum++;
        if (sum>=k) break; 
    }
    printf ("\n");
    return 0;
}

树状数组(常数比较小300ms+):

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int mac=5e6+10;

char s[mac];
int tree[mac],a[mac];

int lowbit(int x){return x&-x;}

void update(int pos)
{
    for (int i=1; i<lowbit(pos); i<<=1){
		tree[pos]=min(tree[pos],tree[pos-i]);
    }
}

int query(int l,int r)
{
    int ans=999;
    while (1){
		ans=min(ans,a[r]);
        if (r==l) break;
        for (r--; r-lowbit(r)>=l; r-=lowbit(r))
            ans=min(ans,tree[r]);
    }
    return ans;
}

int main()
{
    int k;
    scanf ("%s",s+1);
    scanf ("%d",&k);
    int len=strlen(s+1);
    for (int i=1; i<=len; i++)
        a[i]=tree[i]=s[i]-'a';
    for (int i=1; i<=len; i++) update(i);
    int head=1,tail=len-k+1,sum=0;
    for (int i=1; i<=len; i++){
		int p=query(head,tail);
        printf ("%c",p+'a');
        int j;
        for (j=head; j<=tail; j++) if (s[j]=='a'+p) break;
        head=j+1;tail++;
        if (tail>len) tail=len;
        sum++;
        if (sum>=k) break; 
    }
    printf ("\n");
    return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值