bzoj 4278: [ONTAK2015]Tasowanie (后缀数组)

4278: [ONTAK2015]Tasowanie

Time Limit: 10 Sec   Memory Limit: 256 MB
Submit: 161   Solved: 77
[ Submit][ Status][ Discuss]

Description

给定两个数字串A和B,通过将A和B进行二路归并得到一个新的数字串T,请找到字典序最小的T。

Input

第一行包含一个正整数n(1<=n<=200000),表示A串的长度。
第二行包含n个正整数,其中第i个数表示A[i](1<=A[i]<=1000)。
第三行包含一个正整数m(1<=m<=200000),表示B串的长度。
第四行包含m个正整数,其中第i个数表示B[i](1<=B[i]<=1000)。

Output

输出一行,包含n+m个正整数,即字典序最小的T串。

Sample Input

6
1 2 3 1 2 4
7
1 2 2 1 3 4 3

Sample Output

1 1 2 2 1 2 3 1 2 3 4 3 4

HINT

Source

By Claris

[ Submit][ Status][ Discuss]

题解:将两个串连在一起,然后求后缀数组就可以了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 500003
using namespace std;
int n,m,len,p,q,k;
int sa[2][N],rank[2][N],v[N],a[N],b[N];
void solve(int sa[N],int rank[N],int sa1[N],int rank1[N])
{
	for (int i=1;i<=len;i++)  v[rank[sa[i]]]=i;
	for (int i=len;i>=1;i--)
	 if (sa[i]-k>0)
	  sa1[v[rank[sa[i]-k]]--]=sa[i]-k;
	for (int i=len-k+1;i<=len;i++)
	 sa1[v[rank[i]]--]=i;
	for (int i=1;i<=len;i++)
	 rank1[sa1[i]]=rank1[sa1[i-1]]+(rank[sa1[i]]!=rank[sa1[i-1]]||rank[sa1[i]+k]!=rank[sa1[i-1]+k]);
}
void work()
{
	p=0; q=1; 
	for (int i=1;i<=len;i++) v[a[i]]++;
	for (int i=1;i<=1001;i++) v[i]+=v[i-1];
	for (int i=1;i<=len;i++)
	 sa[p][v[a[i]]--]=i;
	for (int i=1;i<=len;i++)
	 rank[p][sa[p][i]]=rank[p][sa[p][i-1]]+(a[sa[p][i]]!=a[sa[p][i-1]]);
	k=1;
	while (k<len){
		solve(sa[p],rank[p],sa[q],rank[q]);
		p^=1; q^=1; k<<=1;
	}
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++) scanf("%d",&a[i]);
	a[n+1]=1001;
	scanf("%d",&m);
	for (int i=1;i<=m;i++)  scanf("%d",&a[n+1+i]);
	len=n+m+1; 
	work();
	int head=1; int head1=n+2;
	int i=1;
	while (i<=n+m&&head<=n&&head1<=n+m+1)
	 {
	 	i++;
	 	if (rank[p][head]<rank[p][head1]) printf("%d ",a[head]),head++;
	 	else  printf("%d ",a[head1]),head1++;
	 }
	for (int i=head;i<=n;i++)
	 printf("%d ",a[i]);
	for (int i=head1;i<=n+m+1;i++)
	 printf("%d ",a[i]);
	printf("\n");
}



你可能感兴趣的:(bzoj 4278: [ONTAK2015]Tasowanie (后缀数组))