初学后缀数组

来源:互联网 发布:mac php环境 编辑:程序博客网 时间:2024/06/01 07:12

后缀数组就是把一个字符串的后缀排序,然后就可以再乱搞一些东东。

DC3看着就累,还是倍增好。

当初看代码觉得虽然不长但是理解起来并不容易,各种数组有各自的意义很容易搞混。

特别是要理解基数排序。每次按第二关键字排序再按第一关键字排序,可以保证在第一关键字相同时第二关键字的大小关系也正确。

白书上的数组有s(字符串)、sa、x、y、c,计算height还用到了rank。

我的代码是照着白书上的写的(发现白书上的代码有几个小的细节问题)。

//Serene#include<algorithm>#include<iostream>#include<cstring>#include<cstdlib>#include<cstdio>#include<cmath>using namespace std;const int maxn=1e6+10;char s[maxn];int n,m,p;int sa[maxn],x[maxn],y[maxn],c[maxn];//sa当前排好的后缀 x每个后缀的rank y由第二关键字排好的后缀 c记录每种相同的个数及前缀和 int height[maxn];int main() {gets(s); n=strlen(s); m='z'+1;for(int i=0;i<n;++i) c[x[i]=s[i]]++;for(int i=1;i<m;++i) c[i]+=c[i-1];for(int i=n-1;i>=0;--i) sa[--c[x[i]]]=i;//sa[c[w]-1]为放入目前rank为w的可放的最后一个位置 for(int k=1;k<=n;k<<=1) {p=0;for(int i=n-1;i>=n-k;--i) y[p++]=i;//把已经没有第二关键字的放在前 for(int i=0;i<n;++i) if(sa[i]>=k) y[p++]=sa[i]-k;//根据第二关键字放入对应的第一关键字位置(后缀的起始位置) for(int i=0;i<m;++i) c[i]=0;for(int i=0;i<n;++i) c[x[i]]++;for(int i=1;i<m;++i) c[i]+=c[i-1];//记录每个后缀第一关键字的信息 for(int i=n-1;i>=0;--i) sa[--c[x[y[i]]]]=y[i];//根据第一关键字排序 swap(x,y);//此时y已经无用,我们需要更新x数组必须用到曾经的x数组(判断相同的情况) p=1; x[sa[0]]=0;for(int i=1;i<n;++i) x[sa[i]]= y[sa[i-1]]==y[sa[i]]&&sa[i-1]+k<n&&sa[i]+k<n&&y[sa[i-1]+k]==y[sa[i]+k]? p-1:p++;if(p>=n) break;else m=p;}for(int i=0;i<p;++i) printf("%d ",sa[i]+1); printf("\n");int k=0;for(int i=0;i<n;++i) {if(!x[i]) {k=0; continue;}if(k) k--;int j=sa[x[i]-1];while(s[i+k]==s[j+k]) k++;height[x[i]]=k;}for(int i=1;i<n;++i) printf("%d ",height[i]);return 0;}


原创粉丝点击