Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined) D. Dense Subsequence ST表+贪心

129 篇文章 0 订阅
58 篇文章 0 订阅

D. Dense Subsequence
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

You are given a string s, consisting of lowercase English letters, and the integer m.

One should choose some symbols from the given string so that any contiguous subsegment of length m has at least one selected symbol. Note that here we choose positions of symbols, not the symbols themselves.

Then one uses the chosen symbols to form a new string. All symbols from the chosen position should be used, but we are allowed to rearrange them in any order.

Formally, we choose a subsequence of indices 1 ≤ i1 < i2 < ... < it ≤ |s|. The selected sequence must meet the following condition: for every j such that 1 ≤ j ≤ |s| - m + 1, there must be at least one selected index that belongs to the segment [j,  j + m - 1], i.e. there should exist a k from 1 to t, such that j ≤ ik ≤ j + m - 1.

Then we take any permutation p of the selected indices and form a new string sip1sip2... sipt.

Find the lexicographically smallest string, that can be obtained using this procedure.

Input

The first line of the input contains a single integer m (1 ≤ m ≤ 100 000).

The second line contains the string s consisting of lowercase English letters. It is guaranteed that this string is non-empty and its length doesn't exceed 100 000. It is also guaranteed that the number m doesn't exceed the length of the string s.

Output

Print the single line containing the lexicographically smallest string, that can be obtained using the procedure described above.

Examples
input
3
cbabc
output
a
input
2
abcab
output
aab
input
3
bcabcbaccba
output
aaabb
Note

In the first sample, one can choose the subsequence {3} and form a string "a".

In the second sample, one can choose the subsequence {1, 2, 4} (symbols on this positions are 'a', 'b' and 'a') and rearrange the chosen symbols to form a string "aab".


Source

Intel Code Challenge Final Round (Div. 1 + Div. 2, Combined)


My Solution

ST表+贪心

ST表,用O(nlogn)的预处理,然后O(1)查询,找出ans中出现的最大的字母,

然后把比它小的字母都从小到大push到ans里,

然后贪心的去那个需要的最大字母的个数,

每次if(maxv == query_min(i, i + m - 1)) 则在这个 [ i, i + m -1 ]区间内找出最右端的一个maxv + 'a'  字母,//如果优先选左边的,则右边的还会取到,所以取区间最右边的maxv+'a'

就好。

复杂度 O(nlogn)


#include <iostream>
#include <cstdio>
#include <string>
#include <cstring>
using namespace std;
typedef long long LL;
const int maxn = 1e5 + 8;

const int MAXN = 1e5 + 8;
int stTable[MAXN][32], preLog2[MAXN], arr[MAXN];

inline void st_prepare(const int &n)
{
    preLog2[1] = 0;
    for(int i = 2; i <= n; i++){
        preLog2[i] = preLog2[i-1];
        if((1 << (preLog2[i] + 1)) == i){
            preLog2[i]++;
        }
    }
    for(int i = n - 1; i >= 0; i--){
        stTable[i][0] = arr[i];
        for(int j = 1; (i + (1 << j) - 1) < n; j++){
            stTable[i][j] = min(stTable[i][j - 1], stTable[i + (1 << j - 1)][j - 1]);
        }
    }
}

inline int query_min(const int &l, const int r)
{
    int len = r - l + 1, k = preLog2[len];
    return min(stTable[l][k], stTable[r - (1 << k) + 1][k]);
}



int cnt[26];
string s, ans;
//bool flag[maxn];

int main()
{
    #ifdef LOCAL
    freopen("d.txt", "r", stdin);
    //freopen("d.out", "w", stdout);
    int T = 4;
    while(T--){
    #endif // LOCAL
    ios::sync_with_stdio(false); cin.tie(0);
    for(int i = 0; i < maxn; i++){
        arr[i] = 1e9 + 7;
    }


    int m, sz;
    cin >> m;
    cin >> s;
    sz = s.size();
    ans.clear();
    for(int i = 0; i < sz; i++){
        cnt[s[i] - 'a']++;
        arr[i] = s[i] - 'a';
        //cout << arr[i+1];
    }

    st_prepare(sz);
    int maxv = -1;
    for(int i = 0; i < sz; i++){
        if(i + m - 1 < sz){
            maxv = max(maxv, query_min(i, i + m - 1));
        }
        else break;
    }

    for(int i = 0; i < maxv; i++){
        while(cnt[i]){
            ans += 'a' + i;
            cnt[i]--;
        }
    }

    for(int i = 0; i < sz; i++){
        //cout << i << endl;
        if(i + m - 1 < sz){
            if(maxv == query_min(i, i + m - 1)){
                for(int j = i + m - 1; j >= i; j--){         //在区域内找到最右边的maxv, 比赛的时候找的最左边的 maxv 尴尬
                    if(s[j] - 'a' == maxv){

                        ans += 'a' + maxv;
                        i = j;
                        //i += m - 1;      //不能跳跃,看样例
                        //cout << i << endl;
                        break;
                    }
                }
            }
        }
        else break;
    }

    cout << ans << endl;


    #ifdef LOCAL
    memset(cnt, 0, sizeof cnt);
    cout << endl;
    }
    #endif // LOCAL
    return 0;
}

  Thank you!

                                                                                                                                               ------from ProLights 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值