【BZOJ-4278】Tasowanie 后缀数组 + 归并

4278: [ONTAK2015]Tasowanie

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit:
164  Solved: 80
[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

Solution

一眼秒后缀数组裸题?算出rank数组来

二路归并的输出答案即可....

PS感觉Claris不会传裸题上来的,于是%了发他的做法,是个贪心

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int read()
{
    int x=0,f=1; char ch=getchar();
    while (ch<‘0‘ || ch>‘9‘) {if (ch==‘-‘) f=-1; ch=getchar();}
    while (ch>=‘0‘ && ch<=‘9‘) {x=x*10+ch-‘0‘; ch=getchar();}
    return x*f;
}
#define maxn 400010
int len,val[maxn];
int S[maxn]; int SA[maxn];
int ws[maxn],wa[maxn],wv[maxn],wb[maxn];
int cmp(int *r,int a,int b,int l)
{
    return r[a]==r[b]&&r[a+l]==r[b+l];
}
void DA(int *r,int *sa,int n,int m)
{
    int p,*x=wa,*y=wb,*t;
    for (int i=0; i<m; i++) ws[i]=0;
    for (int i=0; i<n; i++) ws[x[i]=r[i]]++;
    for (int i=1; i<m; i++) ws[i]+=ws[i-1];
    for (int i=n-1; i>=0; i--) sa[--ws[x[i]]]=i;
    p=1; for (int j=1; p<n; j*=2,m=p)
        {
            p=0; for (int i=n-j; i<n; i++) y[p++]=i;
            for (int i=0; i<n; i++) if (sa[i]>=j) y[p++]=sa[i]-j;
            for (int i=0; i<n; i++) wv[i]=x[y[i]];
            for (int i=0; i<m; i++) ws[i]=0;
            for (int i=0; i<n; i++) ws[wv[i]]++;
            for (int i=1; i<m; i++) ws[i]+=ws[i-1];
            for (int i=n-1; i>=0; i--) sa[--ws[wv[i]]]=y[i];
            t=x; x=y; y=t; p=1; x[sa[0]]=0;
            for (int i=1; i<n; i++)
                x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
        }
}
int rank[maxn],height[maxn];
void calheight(int *r,int *sa,int n)
{
    int k=0;
    for (int i=1; i<=n; i++) rank[sa[i]]=i;
    for (int i=0; i<n; height[rank[i++]]=k)
        {k?k--:0;for (int j=sa[rank[i]-1]; r[i+k]==r[j+k]; k++);}
}
int N,M;
int main()
{
    N=read();
    for (int i=0; i<N; i++) S[i]=read(); S[N]=1001;
    M=read();
    for (int i=1; i<=M; i++) S[i+N]=read(); S[N+M+1]=0;
    DA(S,SA,N+M+2,1002); calheight(S,SA,N+M+1);
    int a=0,b=N+1;
    for (int i=0; i<=N+M; i++)
        {
            if (i==N) continue;
            if (a==N) {for (int j=b; j<=N+M; j++) printf("%d ",S[j]); break;}
            if (b==N+M+1) {for (int j=a; j<N; j++) printf("%d ",S[j]); break;}
            if (rank[a]<rank[b]) printf("%d ",S[a]),a++;
                else printf("%d ",S[b]),b++;
        }
    return 0;
}

跑得怎么比hs(hen)y快这么多..但还是被卡第一页了....

时间: 2024-11-10 11:24:37

【BZOJ-4278】Tasowanie 后缀数组 + 归并的相关文章

【BZOJ4278】[ONTAK2015]Tasowanie 后缀数组

[BZOJ4278][ONTAK2015]Tasowanie 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[

bzoj 2251(后缀数组/后缀自动机)

题意: 给你一个长度为n的01串,问你这个串的所有子串中,出现次数大于1的子串的出现次数,最后按照字典序输出. 分析: 对于这个题目,我们显然可以用两种处理后缀的数据结构进行处理. 1:后缀自动机: 个人觉得在这个题中,用后缀自动机去解决会相对来说比较好理解. 我们知道,在后缀自动机上的结点状态\(st\),若前一个状态通过字符\(c\)与\(st\)相连,那么结点\(st\)表示的是\(endpos\)相同的子串的集合.而该点的\(endpos\)则代表的是以\(c\)为结尾的串的出现的位置集

[BZOJ 1692] [Usaco2007 Dec] 队列变换 【后缀数组 + 贪心】

---恢复内容开始--- 题目链接:BZOJ - 1692 题目分析 首先,有个比较简单的贪心思路:如果当前剩余字符串的两端字母不同,就选取小的字母,这样显然是正确的. 然而若两端字母相同,我们怎么选取呢? 这时我们要从两端分别向内部比较,看那一端向内的字符串字典序小. 比如这个字符串 ABCDBA,从左端向内是 ABC.. 从右端向内是 ABD... 所以就选取左端的字符. 这样直接比较是 O(n^2) 的,我们可以使用后缀数组的 Rank 数组来比较. 我们在字符串后加上分隔符,然后再将字符

bzoj 3172 后缀数组|AC自动机

后缀数组或者AC自动机都可以,模板题. /************************************************************** Problem: 3172 User: BLADEVIL Language: C++ Result: Accepted Time:424 ms Memory:34260 kb ****************************************************************/ //By BLADEVI

BZOJ 3230 相似子串 | 后缀数组 二分 ST表

BZOJ 3230 相似子串 题面 题解 首先我们要知道询问的两个子串的位置. 先正常跑一遍后缀数组并求出height数组. 对于每一个后缀suffix(i),考虑以i开头的子串有多少是之前没有出现过的,也就是考虑左端点在i.右端点在什么范围内时这个子串没有出现过--答案是右端点在[i + height[i] - 1, n]范围内时这个子串没出现过,即右端点在没有被"i与排在前一个的后缀的公共前缀"覆盖的部分时,这个子串没有出现过. 那么我们记录以每个i开头的新子串的数量,求前缀和,然

BZOJ 3238 AHOI 2013 差异 后缀数组+单调栈

题目大意: 思路:一看各种后缀那就是后缀数组没跑了. 求出sa,height之后就可以乱搞了.对于height数组中的一个值,height[i]来说,这个值能够作为lcp值的作用域只在左边第一个比他小的位置到右边第一个比他小的位置.这个东西很明显可以倍增RMQ+二分/单调栈. 之后就是数学题了 Σlen[Ti] + len[Tj] = (len + 1) * len * (len - 1),之后吧所有求出来的Σ2 * lcp(Ti,Tj)减掉就是答案. 记得答案开long long CODE:

后缀数组 hash求LCP BZOJ 4310: 跳蚤

后缀数组的题博客里没放进去过..所以挖了一题写写 充实下博客 顺便留作板子.. 一个字符串S中 内容不同的子串 有 sigma{n-sa[i]+1-h[i]}   (噢 这里的h[]就是大家熟知的height[]) 所以l=1,r=上述sigma 二分 答案是字典序第几大的子串. 然后 求S中第k大的子串W : 因为h[i]是与i-1有关的 所以要从n downto 1,k-=n-sa[i]+1-h[i] 至 k再减就非正了 显然这样扫过来 子串字典序是递减的  因此可以得到第k大子串W 然后再

BZOJ 4199: [Noi2015]品酒大会( 后缀数组 + 并查集 )

求出后缀数组后, 对height排序, 从大到小来处理(r相似必定是0~r-1相似), 并查集维护. 复杂度O(NlogN + Nalpha(N)) ----------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> using namespace std; ty

BZOJ 3230: 相似子串( RMQ + 后缀数组 + 二分 )

二分查找求出k大串, 然后正反做后缀数组, RMQ求LCP, 时间复杂度O(NlogN+logN) --------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<cstring> #include<cctype> using namespace std; typedef long long