题意
给定一个字符串,每次珂以取出当前字符串的头或者尾,将其加入答案串中(放到最后面),要求必须把该字符串取完,求字典序最小的方案
分析
SA简单题
题目要求的是求字典序最小,很容易想到SA来求
我们观察一下,对于每一位可能的情况就只有两种,所以我们想到贪心地取,每次取当前头和尾当中较小的那个字符
但是这样会出现一个问题:如果头和尾相同该怎么办?
比如这样的一个字符串:BCAB
现在我们取这个串就不知道该取头还是尾,这个时候如果我们贸然取头的话(变成CAB),我们下一步只能取到C或B(当前就应该取B)
这样干之后我们会取到BB作为当前已经取了的,实际上我们有更优的选择,那就是第一次取的时候取尾,然后我们珂以取到BA这个串,它的字典序更小
那么我们考虑在这种情况下怎么搞。
珂以发现,其实每次取都是在查看当前状态下,是当前串的字典序小,还是当前反串的字典序小,那么这就相当于是比较两个字符串的字典序谁大谁小的问题
很容易想到后缀数组SA来做。
具体就是设原串为A,设A的反串是~A,那么此时我们考虑拼接这样的一个字符串 B=A+(分隔符)+ ~A
此时我们对B串跑一边SA就可以得到原串和反串字典序排序之后的相对大小了(rk数组)
每次选字符的时候(O(1))比较一下就行了
代码
#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
x=0;char ch=getchar();bool f=false;
while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
x=f?-x:x;
return ;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){read(t);read(args...);}
template <typename T>
inline void write(T x){
if(x<0) putchar('-'),x=-x;
if(x>9) write(x/10);
putchar(x%10^48);
return ;
}
template <typename T>
inline void print(T x){write(x),putchar(' ');}
#define ll long long
#define ull unsigned long long
#define inc(x,y) (x+y>=MOD?x+y-MOD:x+y)
#define dec(x,y) (x-y<0?x-y+MOD:x-y)
#define min(x,y) (x<y?x:y)
#define max(x,y) (x>y?x:y)
const int N=1e6+5,M=1e6+5,MOD=1e9+7;
char str[N];
int n,m,k,sa[N],rk[N],oldrk[N],id[N],px[N],cnt[N];
bool cmp(int x,int y,int w){return oldrk[x]==oldrk[y] && oldrk[x+w]==oldrk[y+w];}
void SA(){
int i,p=0,w;
m=300;
for(i=1;i<=n;i++) ++cnt[rk[i]=str[i]];
for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--) sa[cnt[rk[i]]--]=i;
for(w=1;w<n;w<<=1,m=p){
for(p=0,i=n;i>n-w;i--) id[++p]=i;
for(i=1;i<=n;i++) if(sa[i]>w) id[++p]=sa[i]-w;
memset(cnt,0,sizeof(cnt));
for(i=1;i<=n;i++) ++cnt[px[i]=rk[id[i]]];
for(i=1;i<=m;i++) cnt[i]+=cnt[i-1];
for(i=n;i>=1;i--) sa[cnt[px[i]]--]=id[i];
memcpy(oldrk,rk,sizeof(rk));
for(p=0,i=1;i<=n;i++) rk[sa[i]]=cmp(sa[i],sa[i-1],w) ? p : ++p;
}
return ;
}
int main(){
read(k);char ch;
for(int i=1;i<=k;i++) while(isalpha(ch=getchar())) str[i]=ch;
n=k<<1|1;
for(int i=1;i<=k;i++) str[i+k+1]=str[k-i+1];
SA();
int l=1,r=k,tot=0;
while(l<=r){
if(rk[l]<rk[n+1-r]) putchar(str[l++]);
else putchar(str[r--]);
if((++tot)%80==0) puts("");
}
system("Pause");
return 0;
}