题意
给定两个数字串A和B,要求归并得到一个字典序最小的数字串T(长度<200000)
分析
(大水题)
a1,a2,a3...an,1001,b1,b2,b3...bm,0
搞一波后缀数组,再用两指针扫一遍就好了。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1001000;
int sa[N],tsa[N],rk[N],trk[N],sum[N];
int len1,len2,cnt,m,s[N];
int main(){
freopen("a.in","r",stdin);
scanf("%d",&len1);
for (int i=1;i<=len1;i++)
scanf("%d",s+i);
scanf("%d",&len2);s[len1+1]=1001;
for (int i=len1+2;i<=len1+len2+1;i++)
scanf("%d",s+i);s[len1+len2+2]=0;
int len=len1+len2+2;
for (int i=1;i<=len;i++) sum[s[i]]++;
for (int i=1;i<=1001;i++) sum[i]+=sum[i-1];
for (int i=len;i;i--) sa[sum[s[i]]--]=i;
rk[sa[1]]=1;cnt=1;
for (int i=2;i<=len;i++){
if (s[sa[i]]!=s[sa[i-1]]) cnt++;
rk[sa[i]]=cnt;
}m=cnt;
for (int j=1;m<len;j*=2){
cnt=0;
memset(sum,0,sizeof(sum));
memmove(trk,rk,sizeof(rk));
for (int i=len-j+1;i<=len;i++) tsa[++cnt]=i;
for (int i=1;i<=len;i++)
if (sa[i]>j) tsa[++cnt]=sa[i]-j;
for (int i=1;i<=len;i++) sum[ trk[tsa[i]] ]++;
for (int i=1;i<=m;i++) sum[i]+=sum[i-1];
for (int i=len;i;i--) sa[sum[ trk[tsa[i]] ]--]=tsa[i];
rk[sa[1]]=1;cnt=1;
for (int i=2;i<=len;i++){
if (trk[sa[i]]!=trk[sa[i-1]] || trk[sa[i]+j]!=trk[sa[i-1]+j]) cnt++;
rk[sa[i]]=cnt;
}m=cnt;
}
int t1=1,t2=2+len1;
for (int i=1;i<=len-2;i++){
if (t1==len1+1) printf("%d ",s[t2]),t2++; else
if (t2==len1+len2+2) printf("%d ",s[t1]),t1++; else
if (rk[t1]<rk[t2]) printf("%d ",s[t1]),t1++; else
printf("%d ",s[t2]),t2++;
}
printf("\n");
}