bzoj 4278 Tasowanie —— 后缀数组
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4278
每次取两个后缀中字典序较小的那个的首字符;
注意超出去的部分是 inf 而不是 0,因为如果到了比较超出去部分的时候,那就是一个串走到了末尾而另一个没有(或者都到末尾,不过都到末尾就随便选啦),而且两个后缀的 LCP 顶到了末尾;
这时选长度较长的比较优,因为反正 LCP 处一样,先选较短的等选完以后还是得过来,不如先选较长的,也许后面还出现更优的。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; int const xn=4e5+5; int n,m,mx,a[xn>>1],b[xn>>1],s[xn],tp[xn],sa[xn],rk[xn],tax[xn]; int rd() { int ret=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=0; ch=getchar();} while(ch>='0'&&ch<='9')ret=ret*10+ch-'0',ch=getchar(); return f?ret:-ret; } void Rsort() { int len=n+m; for(int i=1;i<=mx;i++)tax[i]=0; for(int i=1;i<=len;i++)tax[rk[tp[i]]]++; for(int i=1;i<=mx;i++)tax[i]+=tax[i-1]; for(int i=len;i;i--)sa[tax[rk[tp[i]]]--]=tp[i]; } int ps(int nw,int k) { if(nw<=n&&nw+k>n)return 0; return nw+k; } void work() { int len=n+m; for(int i=1;i<=len;i++)rk[i]=s[i],tp[i]=i; Rsort(); int up=max(n,m); for(int k=1;k<=up;k<<=1) { int num=0; //for(int i=len-k+1;i<=len;i++)tp[++num]=i; //for(int i=n-k+1;i<=n;i++)tp[++num]=i; for(int i=1;i<=len;i++) if((sa[i]<=n&&sa[i]>k)||(sa[i]>n&&sa[i]>n+k))tp[++num]=sa[i]-k; for(int i=len-k+1;i<=len;i++)tp[++num]=i; for(int i=n-k+1;i<=n;i++)tp[++num]=i; Rsort(); swap(rk,tp); rk[sa[1]]=1; num=1; for(int i=2;i<=len;i++) rk[sa[i]]=(tp[sa[i]]==tp[sa[i-1]]&&tp[ps(sa[i],k)]==tp[ps(sa[i-1],k)])?num:++num; if(num==len)break; mx=num; } } int main() { n=rd(); for(int i=1;i<=n;i++)a[i]=rd(),s[i]=a[i],mx=max(mx,a[i]); m=rd(); for(int i=1;i<=m;i++)b[i]=rd(),s[n+i]=b[i],mx=max(mx,b[i]); work(); int p1=1,p2=1; while(p1<=n&&p2<=m) { if(rk[p1]<rk[n+p2])printf("%d ",a[p1++]); else printf("%d ",b[p2++]); } if(p1>n)while(p2<=m)printf("%d ",b[p2++]); if(p2>m)while(p1<=n)printf("%d ",a[p1++]); puts(""); return 0; }