【AtCoder Grand Contest 037 E】Reversing and Concatenating
贪心,构造。
题目大意:
给定长度为 $n$ 的字符串 $s$,和 $k$,要求进行 $k$ 次如下操作:
- 令 $s’=s+\mathrm{reverse}(s)$。
- 令 $s$ 为 $s’$ 中一个长度为 $n$ 的子串。
要求最小化 $k$ 次操作后 $s$ 的字典序。
题解:
我们考虑贪心地构造答案。
第一次操作的时候,我们把一个最小的字符放到 $s$ 的末尾。那么第二次操作的时候,$s’$ 中就有了两个连续的最小字符。我们把它放末尾,重复这个操作。这个字符的个数每次扩大一倍,所以在 $1+\lceil\log_2 n\rceil$ 次内,整个串都可以变为最小的字符。字典序显然无法更小。
当 $k$ 很小的时候,我们直接模拟这个过程。我们发现,这个过程实际上相当于每次取 $s’$ 中反串字典序最小的,长度为 $n$ 的串作为新的 $s$,所以可以 $O(n^2)$ 完成。
时间复杂度 $O(n^2\log n)$。
Code:
#include<iostream>
#include<string>
#include<algorithm>
using namespace std;
string s;
int n,k;
int main(){
cin>>n>>k>>s;
s+=string(s.rbegin(),s.rend());
if(k>20)cout<<string(n,*min_element(s.begin(),s.end()))<<'\n';else{
while(k--){
string t(s,0,n);
reverse(t.begin(),t.end());
for(int i=1;i<=n;++i){
string p(s,i,n);
reverse(p.begin(),p.end());
if(p<t)t=p;
}
s=string(t.rbegin(),t.rend())+t;
}
s.erase(0,n);
cout<<s<<'\n';
}
return 0;
}