「BZOJ4278」[ONTAK2015]Tasowanie-后缀数组

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

链接

BZOJ
(离线题面)
data(数据)

题解

如果前面两个字符不同,显然选取小的一个。

分情况讨论,我们可以发现,如果相同,那么就应该取目前后缀字典序较小的一个。

所以事实上就是取后缀字典序比较小的。

此题亦可hash二分。

代码

点击切换显示状态
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <cstdio>
using namespace std;

const int MAXN = 410000;

namespace SA{
int s[MAXN],sa[MAXN],ht[MAXN],rk[MAXN],x[MAXN],y[MAXN];
int cnt[MAXN];
void get_sa(int n,int m){
for(int i = 0;i<m;i++) cnt[i] = 0;
for(int i = 0;i<n;i++) cnt[s[i]]++;
for(int i = 1;i<m;i++) cnt[i] += cnt[i-1];
for(int i = n-1;~i;--i) sa[--cnt[s[i]]] = i;
m = rk[sa[0]] = 0;
for(int i = 1;i<n;i++) rk[sa[i]] = s[sa[i]] != s[sa[i-1]]?++m:m;
for(int j = 1;;j<<=1){
if(++m == n) break;
for(int i = 0;i<j;i++) y[i] = n-j+i;
for(int i = 0,k = j;i<n;i++) if(sa[i] >= j) y[k++] = sa[i] - j;
for(int i = 0;i<n;i++) x[i] = rk[y[i]];
for(int i = 0;i<m;i++) cnt[i] = 0;
for(int i = 0;i<n;i++) cnt[x[i]]++;
for(int i = 1;i<m;i++) cnt[i] += cnt[i-1];
for(int i = n-1;~i;--i) sa[--cnt[x[i]]] = y[i];
m = rk[sa[0]] = 0;
for(int i = 1;i<n;i++) rk[sa[i]] = (y[sa[i]] != y[sa[i-1]] || y[sa[i]+j] != y[sa[i-1]+j])?++m:m;
}
}
void build(int n,int* str){
int m = 1002;str[n++] = 0;
for(int i = 0;i<n;i++) s[i] = str[i];
get_sa(n,m);
}
bool cmp(int i,int j){
return rk[i+1] < rk[j+1];
}
}

int n;
int u,v;
int a[MAXN],b[MAXN],t[MAXN],ans[MAXN];

void init(){
scanf("%d",&u);
for(int i = 0;i<u;i++) scanf("%d",&a[i]);
scanf("%d",&v);
for(int i = 0;i<v;i++) scanf("%d",&b[i]);
for(int i = 0;i<u;i++) t[n++] = a[i];
t[n++] = 0;
for(int i = 0;i<v;i++) t[n++] = b[i];
SA::build(n,t);
}

void solve(){
int l = 0,r = 0,t = 0;
while(t <= u+v){
// printf("l:%d,%d r:%d,%d\n",l,a[l],r,b[r]);
if(l == u) ans[t++] = b[r++];
else if(r == v) ans[t++] = a[l++];
else{
if(a[l]==b[r])
ans[t++] = SA::cmp(l,u+1+r)?a[l++]:b[r++];
else
ans[t++] = a[l] < b[r]?a[l++]:b[r++];
}
}
for(int i = 0;i<u+v;i++)
printf("%d ",ans[i]);
printf("\n");
}

int main(){
init();
solve();
return 0;
}