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;
}

 

posted @ 2018-12-06 11:33  Zinn  阅读(122)  评论(0编辑  收藏  举报