一、题目:
二、思路:
这题不知道为什么官方题解写得那么复杂。还需要用到扩展 KMP,即 Z 函数。感谢 Robert_JYH 巨佬提供的思路,让我明白了一种非常简单的做法。
首先,我们可以证明,最优解一定是通过先执行操作 1,后执行操作 2 来得到的(当然也可以不进行操作 1,直接进行操作 2)。因为如果最优解是执行了一些操作 2 之后,再执行的操作 1,那么我们发现把操作 1 放到操作 2 之前操作一定会使答案变得不劣。
所以问题就转化成给定一个字符串 (s),需要保留 (s) 的一个前缀 (pre),同时让 (pre) 复制若干次,得到一个长度为 (k) 的字符串(多余的部分删去)。目标是让最终的字符串的字典序最小。
暴力当然很简单了,我们枚举所有前缀 (pre),模拟这个过程,更新答案即可。你就可以成功地解决这道题的简单版了。
那么现在来考虑线性的做法。
我们设当前最优的前缀为 (s[0sim p-1]),现在枚举到的前缀是 (s[0sim i])。那么分为三种情况。(字符串的下标从 0 开始。)
- 若 (s[i]>s[imod p]),那么非常遗憾,(i) 及 (i) 以后的所有前缀均不能作为最优答案。直接跳出循环。
- 若 (s[i]<s[imod p]),我们发现用前缀 (s[0sim i]) 比前缀 (s[0sim p-1]) 更优,于是令 (pgets i+1)。
- 若 (s[i]=s[imod p]),这并不能说明什么,我们只能什么都不做。
然后呢?然后你就会惊奇的发现,你写完了一道 Div2 难度的第六题!
三、代码:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
#define FILEIN(s) freopen(s".in", "r", stdin);
#define FILEOUT(s) freopen(s".out", "w", stdout)
#define mem(s, v) memset(s, v, sizeof s)
inline int read(void) {
int x = 0, f = 1; char ch = getchar();
while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
return f * x;
}
int n, K;
string s;
int main() {
n = read(); K = read();
cin >> s;
int p = 1, i = 0, n = s.length();
for (; i < n; ++ i) {
if (s[i] > s[i % p]) break;
if (s[i] < s[i % p]) p = i + 1;
}
for (i = 0; i < K; ++ i) putchar(s[i % p]);
puts("");
return 0;
}