[CF1975H] 378QAQ and Core Solution
好有趣的一个题!本来在想怎么实现 std 中那个递归做法,然后翻到了一个厉害的提交记录,发现这个题完全可以做线性代码也很好写,所以在这里记录一下。
题目中的 Core 讲的是一个串的最大后缀。这个最大后缀肯定以最大字符(设为
但往往
所以最优的字符串结构形如
值得一提的是,这个子问题的单个字符变成了一个字符串,而字符串之间的大小比较不是直接的字典序比较,真正的比较关系
我们将字符串离散化之后可以递归下去做。现在的问题就变成了如何确定一个能生成最优解的
第一个观察是由于
设原串有
精细实现上述做法,由于每次递归规模减半(
我们有更高明的实现!!注意到我们递归做法中经常重复扫一些已经不是最大值的
我们维护一个有序的无穷字符串数列(这个无穷字符串的比较同我们上面所讲的比较),一开始所有位置都是单字符(后面跟一堆
然后我们从左到右扫描这个序列
看起来很神秘?但玩一玩可以发现这和上面的递归做法流程完全等价!只是减少了许多不必要的字符串缩减成一个字符和字符串的移动!
这个过程的复杂度是什么呢?观察发现在这个过程中
具体讲下这两个做法为什么本质一样:当每次扫到的
My Code:
#include <list>
#include <cstdio>
using namespace std;
const int N=1000003;
const int B=29,P=1000000009;
typedef long long ll;
int n;
char s[N];
int pw[N];
struct node{
list<int> s;int val;
node(){}
node(int c):s(1,c),val(c+1){}
void operator+=(node &x){
s.splice(s.end(),x.s);
val=((ll)val*pw[x.s.size()]+x.val)%P;
}
}o[N];
int f[N],cnt[27];
void solve(){
scanf("%d%s",&n,s+1);
for(int i=1;i<=n;++i) ++cnt[s[i]-96];
pw[0]=1;
for(int i=1;i<=n;++i) pw[i]=(ll)pw[i-1]*B%P;
o[0]=node(0);n=0;
for(int c=1;c<=26;++c) while(cnt[c]) --cnt[c],o[++n]=node(c);
int ps=n;
for(int i=0;i<=n&&o[n].s.back();++i){
if(ps==n) while(ps>i&&o[ps].val==o[n].val) --ps;
o[++ps]+=o[i];
}
for(int c:o[n].s) if(c) putchar(c+96);
putchar('\n');
}
int main(){
int tc;
scanf("%d",&tc);
while(tc--) solve();
return 0;
}