AGC037E Reversing and Concatenating
Description
Solution
要求字典序最小,那么把原字符串中最小的移到开头一定是最优的。
同时为了使字典序尽量小,我们要使字符串中开头最小的字符串的数量尽可能的多且连续,由于是把字符串的反串放到字符后面,那我们先把最小的字符移动到字符串结尾,然后截取若干段最小字符和前面的一些字符,我们会得到结尾是若干最小字符的新串,这样反复操作,直到最后一次操作时截取开头是若干最小字符的字符串一定是最优的。
把最小的字符移动到结尾保证了开头字符最小,正串和反串接起来最后一段会加倍保证了连续的最小字符数量最多。
这样我们除第一次外每次操作都能使得字符串中最小的字符的个数翻倍,那么过不了多久整个字符串就全变成了最小字符。
所以可以暴力模拟整个过程,具体的找到原串中连续最小字符最多的一个子串用一次操作移动到结尾,然后每次操作加倍,注意到如果最后不是整个串都是最小字符,后面会剩下原串的一些东西,其实每次可以暴力把这些东西提出来比较,但是也可以在最开始把反串接到原串后面找到最小的位置,代码量大幅度减少。
Code
/*
_______ ________ _______
/ _____ \ / ______ \ / _____ \
/ / \_\ _ __ _ / / \ \ _ __ _ / / \_\
| | | | | | | | | | | | | | | | | | | |
| | | | | | | | | | __ | | | | | | | | | |
| | __ \ \ | | / / | | \ \| | \ \ | | / / | | __
\ \_____/ / \ \/ /\ \/ / \ \_____\ / \ \/ /\ \/ / \ \_____/ /
\_______/ \___/ \___/ \______/\__\ \___/ \___/ \_______/
*/
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N = 5000;
int n, k;
char st[N * 2 + 50];
int Cmp(int a, int b)
{
for (int i = 1; i <= n; i++)
{
if (st[a - i + 1] < st[b - i + 1]) return 1;
if (st[a - i + 1] > st[b - i + 1]) return 2;
}
return 0;
}
int main()
{
scanf("%d%d", &n, &k);
scanf("%s", st + 1);
for (int i = 1; i <= n; i++) st[n * 2 - i + 1] = st[i];
int pos = n;
for (int i = n + 1; i <= n * 2; i++) if (Cmp(i, pos) == 1) pos = i;
k--;
int len = 1;
while (st[pos - len] == st[pos] && len + 1 <= n) len++;
int tmp = len;
int ned = 0;
while (len < n && ned + 1 <= k) len *= 2, ned++;
if (len >= n) for (int i = 1; i <= n; i++) putchar(st[pos]);
else
{
for (int i = 1; i <= len; i++) putchar(st[pos]);
for (int i = 1; i <= n - len; i++) putchar(st[pos - tmp + 1 - i]);
}
return 0;
}