CF1527 E2. Erase and Extend (Hard Version)
题意:
给出一个长为n的字符串,你可以进行2种操作
1、把串在后面再拼接一次
2、删去串的最后一个元素
要求用这两种操作得出字典序最小的长为m的串
首先答案肯定是由一个前缀拼接多次构成
假设当前的最优前缀是A前缀,长度为LA,现在正在考虑的是B前缀
1、如果s[B%LA]和s[A]一样,那么不用管继续往后走
2、如果s[B%LA]比s[A]小,那么B前缀比A前缀更优秀
3、如果s[B%LA]比s[A]大,那么A前缀比B前缀更优秀
先来看2和3的正确性
在1的条件下,他们都包含了s[0…A]拼接多次=s[0…B-1]
所以只需要比较s[B%LA]与s[A]即可
然后是1的正确性,它包含了s[0…A]拼接多次=s[0…B]
令B%LA=t,可以把前B的字符分为3段
s[0…t]一段记为X,s[t+1,LA-1]一段记为Y,s[LA…B]一段等同于s[0…t]
如果还是用A前缀,那么拼接结果是XYXYXYXY
如果改为用B前缀,那么拼接结果是XYXXYXXY
所以如果YX<XY,那么还是原先的A前缀
如果XY<YX,才会改用B前缀
但是如果XY<YX,XY的拼接就不会是最小的,因为X的拼接会给更小
#include<bits/stdc++.h> using namespace std; #define N 500003 char s[N]; int main() { int n,m; scanf("%d%d",&n,&m); scanf("%s",s); int len=1; for(int i=1;i<n;++i) if(s[i]<s[i%len]) len=i+1; else if(s[i]>s[i%len]) break; int now=0; for(int i=1;i<=m;++i) { printf("%c",s[now]); ++now; if(now==len) now=0; } }