后缀数组

# 题意
详细地说,给定一个长度为 n 的字符串S(下标 0~n-1),我们可以用整数 k(0≤k<n0≤k<n) 表示字符串S的后缀 S(k~n-1)。
把字符串S的所有后缀按照字典序排列,排名为 i 的后缀记为 SA[i]。
额外地,我们考虑排名为 i 的后缀与排名为 i-1 的后缀,把二者的最长公共前缀的长度记为 Height[i]。
我们的任务就是求出SA与Height这两个数组。

# 题解
开始每个SA都是当前的下标
定义比较函数是从两个后缀的公共长度后第一个字符的大小比较
用二分求最长公共序列长度,
因为是最长用最后一个元素的二分板子
排序过后
再依次输出字典序排在第 i 位的的后缀的开头下标
已经他和他前面一个的最长公共序列长度

 1 #include <bits/stdc++.h>
 2 #define ull unsigned long long
 3 using namespace std;
 4 const int N=3e5+10,P=131;
 5 char s[N];
 6 int sa[N];
 7 ull h[N],p[N];
 8 int n;
 9 ull get(int l,int r){
10    return h[r]-h[l-1]*p[r-l+1];
11 }
12 int get_lcp(int a,int b){
13    int l=0,r=min(n-a+1,n-b+1);
14    while(l<r){
15       int mid=l+r+1>>1;
16       if(get(a,a+mid-1) == get(b,b+mid-1))
17          l=mid;
18       else
19          r=mid-1;
20    }
21    return l;
22 }
23 bool cmp(int a,int b){
24    int len=get_lcp(a,b);
25    int ava=a+len>n?INT_MIN:s[a+len];
26    int bva=b+len>n?INT_MIN:s[b+len];
27    return ava<bva;
28 }
29 int main(){
30    ios::sync_with_stdio(0);
31    cin.tie(0);
32    cout.tie(0);
33 
34    scanf("%s",s+1);
35    n=strlen(s+1);
36    p[0]=1;
37    for(int i=1;i<=n;i++){
38       h[i]=h[i-1]*P+s[i]-'a'+1;
39       p[i]=p[i-1]*P;
40       sa[i]=i;
41    }
42    sort(sa+1,sa+n+1,cmp);
43 
44    for(int i=1;i<=n;i++)
45       cout<<sa[i]-1<<' ';
46    cout<<endl;
47 
48    for(int i=1;i<=n;i++){
49       if(i==1)
50          cout<<"0 ";
51       else
52          cout<<get_lcp(sa[i-1],sa[i])<<' ';
53    }
54    cout<<endl;
55 }

 

 

 

posted @ 2020-03-18 00:35  Hyx'  阅读(189)  评论(0编辑  收藏  举报