CH1402 后缀数组

题意

后缀数组 (SA) 是一种重要的数据结构,通常使用倍增或者DC3算法实现,这超出了我们的讨论范围。在本题中,我们希望使用快排、Hash与二分实现一个简单的 O(n log^2⁡n ) 的后缀数组求法。详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n) 表示字符串S的后缀 S(k~n-1)。把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。我们的任务就是求出SA与Height这两个数组。

字符串,长度不超过30万。

分析

此题数据范围较小,可以用hash。

求sa:sort+二分hash加速比较,时间复杂度\(O(N \log^2 N)\)

求height:二分hash,时间复杂度\(O(N \log N)\)

明显比倍增+基数排序慢。

代码

#include<bits/stdc++.h>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
    rg T data=0,w=1;
    rg char ch=getchar();
    while(!isdigit(ch)){
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(isdigit(ch))
        data=data*10+ch-'0',ch=getchar();
    return data*w;
}
template<class T>il T read(rg T&x){
    return x=read<T>();
}
typedef unsigned long long ull;
using namespace std;

co int N=3e5+1,P=131;
ull f[N],power[N];
int sa[N],rk[N],height[N],n;
char str[N];
ull get_hash(int l,int r){
    return f[r]-f[l-1]*power[r-l+1];
}
int lcp(int x,int y){
    int l=0,r=min(n-x+1,n-y+1);
    while(l<r){
        int mid=(l+r+1)>>1;
        if(get_hash(x,x+mid-1)==get_hash(y,y+mid-1)) l=mid;
        else r=mid-1;
    }
    return l;
}
bool cmp(int x,int y){
    int l=lcp(x,y);
    return str[x+l]<str[y+l];
}
void calc_height(){
    for(int i=2;i<=n;++i){
        height[i]=lcp(sa[i-1],sa[i]);
    }
}
int main(){
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    scanf("%s",str+1);
    n=strlen(str+1);
    n=strlen(str+1);
    power[0]=1;
    for(int i=1;i<=n;++i){
        sa[i]=i;
        f[i]=f[i-1]*P+str[i];
        power[i]=power[i-1]*P;
    }
    sort(sa+1,sa+n+1,cmp);
    calc_height();
    for(int i=1;i<=n;++i)
        printf("%d ",sa[i]-1);
    puts("");
    for(int i=1;i<=n;++i)
        printf("%d ",height[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/autoint/p/10399830.html