人类智慧题,对于这种题确实不太会。接受现实,多加练习(
这题一眼看过去没有什么思路或性质。需要字典序最小,我们只能从最小的字符开始考察,设其为
。
我们的首要目标是在最后最大化最开头那段 的长度。
如果在翻转之前,有一段 处在
的末尾,那么翻转过后,其会变成原来的两倍长,如果处在其它地方,你只能多得到一段长度相等的
。
所以,对于 ,只需输出 个 即可。
对于剩下的情况,我们可以用开头的一次操作把最长的
放到串的末尾,然后不断倍长,在最后一次操作时把这段 放在结果串的开头。
接下来我们要让
后面的那一段的字典序最小。
设那段最长的 为 ,它前面的串为 。我们先用一次操作把 提到 的最后,再一次操作后变为 ,容易发现,新的串必然是 的一段后缀,如此下去,最后的串必然是
的一段前缀,我们只需找出字典序最小的 。
有意思的地方是,由于
为回文串,只要 在 中出现, 一定会在 中出现,那么我们找字典序最小的 ,只需在 中找到字典序最小的长度为 的子串即可。这个可以 完成。
上面的做法也统一了
的情况,最终的时间复杂度为 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| #include <bits/stdc++.h> using namespace std; const int N = 5e3 + 5; int n,k; char s[N << 1]; char ans[N]; inline void Useans(int p) { for(int i = 1;i <= n;i++) ans[i] = s[p + i - 1]; } int fir; inline void Check(int p) { if(!fir) Useans(p),fir = true; for(int i = 1;i <= n;i++) { if(ans[i] < s[i + p - 1]) return; if(ans[i] > s[i + p - 1]) return Useans(p); } } int main() { cin >> n >> k; cin >> (s + 1); if(k >= 15) { char c = 'z'; for(int i = 1;i <= n;i++) c = min(c,s[i]); for(int i = 1;i <= n;i++) putchar('c'); putchar('\n'); return 0; } for(int i = 1;i <= n;i++) s[n * 2 + 1 - i] = s[i]; for(int i = 1;i <= n + 1;i++) Check(i); int mx = 0; for(int i = 1;i <= n + 1;i++) if(ans[i] == ans[1]) ++mx; else break; int len = mx << (k - 1); for(int i = 1;i <= len;i++) putchar(ans[1]); for(int i = len + 1;i <= n;i++) putchar(ans[i - len + mx]); putchar('\n'); return 0; }
|