CF724D 最小字典序

1 CF724D 最小字典序

2 题目描述

时间限制 \(2s\) | 空间限制 \(256M\)

给出一个由小写字母组成的英文字符串 \(s\) 和整数 \(m\)。从给定的字符串中选择一些字符,使得长度为 \(m\) 的任何相邻子段至少有一个选定的字符。注意在这里我们选择字符的位置,而不是字符本身。然后使用所选的字符来组成一个新的字符串。选中位置的所有字符都应该用上,当然我们可以重新安排顺序。形式上,我们选择一个序列 \(1 ≤ i_1 < i_2 < ... < i_t ≤ |s|\) 的子序列。选中的子序列必须满足下面的条件:对于所有 \(j, 1 ≤ j ≤ |s| - m + 1\), 必须至少有一个索引在 \([j,  j + m - 1]\) 范围内,即一定存在一个 \(k\)\(1\)\(t\),使得 \(j ≤ i_k ≤ j + m - 1\)。然后我们取所选索引的任何排列 \(p\),形成一个新的字符串 \(s_{i_{p_1}}s_{i_{p_2}}... s_{i_{p_t}}\)。找出可以使用此操作获得的最小字典序字符串。

数据范围:\(1 ≤ m ≤ 100000\)

3 题解

容易发现:无论如何,我们都要从字典序小的字母枚举到字典序大的字母。因为即使字典序大的字母只用一个字母即可满足题目要求,它的字典序仍然大于一万个更小的字典序的字母构成的字符串的字典序大。所以我们可以枚举 \(26\) 个字母,如果当前字母全部选中后仍然不满足条件,那么我们继续枚举下一个字母。否则我们具体看选多少个该字母。

在循环过程中,我们如果发现了某一个长度为 \(m\) 的没有选中任何字母的区间,那么我们就可以找到这个区间中最靠后的当前字母,将字母个数 \(+1\) 即可。这是因为,我们找到最靠后的当前字母可以让我们在满足当前区间合法的情况下尽可能让后面的区间合法。最后输出之前的所有字母和若干个当前字母即可。

4 代码(空格警告):

#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e5+10;
int m, End, cnt, cnt2, cnt3, cnt4, last, ans;
int num[30], num2[N];
string s;
bool f[N], f2[N], flag;
int main()
{
    memset(num2, 0x3f, sizeof(num2));
    cin >> m;
    cin >> s;
    for (int i = 0; i < s.length(); i++) num[s[i] - 'a']++;
    for (int i = 0; i < 26; i++)
    {
        char x = char('a' + i);
        for (int j = 0; j < s.length(); j++)
        {
            if (s[j] == x) f[j] = 1;
        }
        flag = 0;
        cnt = 0;
        for (int j = 0; j < s.length(); j++)
        {
            if (!f[j]) cnt++;
            if (f[j]) cnt = 0;
            if (cnt == m) flag = 1;
        }
        if (!flag)
        {
            End = i;
            break;
        }
    }
    memset(f, 0, sizeof(f));
    for (int i = 0; i < s.length(); i++)
    {
        if (s[i] - 'a' < End) f[i] = 1;
    }
    for (int i = 0; i < End; i++)
    {
        for (int j = 1; j <= num[i]; j++) cout << char('a' + i);
    }
    cnt = 0;
    last = -1;
    for (int i = 0; i < s.length(); i++)
    {
        if (s[i] == char('a' + End))
        {
            num2[++cnt2] = i;
        }
    }
    cnt3 = 1;
    for (int i = 0; i < s.length(); i++)
    {
        if (f[i]) last = i;
        if (i - last == m)
        {
            while (num2[cnt3] <= i) cnt3++;
            cnt3--;
            ans++;
            last = num2[cnt3];
        }
    }
    for (int i = 1; i <= ans; i++) cout << char('a' + End);
    return 0;
}

欢迎关注我的公众号:智子笔记

posted @ 2021-03-26 01:29  David24  阅读(54)  评论(0编辑  收藏  举报