浅谈后缀数组(对模板的理解
显然可知对于后缀数组最难理解的就是sa数组和rank数组的构建,而sa与rank数组之间有一个相互转化关系,这就必须理解sa数组和rank数组的含义,sa数组代表的是排名为i的后缀第一个字符所在的位置,而rank数组则表示的是第i个位置的后缀的排名;通过这个关系,我们可以实现sa与rank之间的转化,所以整个关键就成了如何去求他们其中的一个,根据,我的学习笔记来源这几个大神的博客可做参考:
1,http://blog.csdn.net/jokes000/article/details/7839686;
2,http://www.cnblogs.com/shanchuan04/p/5324009.html;前者具有深刻的代码分析及应用,后者更为形象的理解后缀数组;
下面之间贴上我自己对于后缀数组的理解:
#include<algorithm> #include<iostream> #include<queue> #include<stack> #include<vector> #include<set> #include<map> #include<cstdio> #include<cstring> #define MAXN 100005 using namespace std; char a[MAXN]; int rank[MAXN];int td[MAXN];int sa[MAXN];int Rank[MAXN];int txt[MAXN];int t1[MAXN];int t2[MAXN]; int height[MAXN];int h[MAXN]; bool cmp(int t[],int e,int w,int f){ return t[e]==t[w]&&t[e+f]==t[w+f]; } void Sa(char a[]){ int *rank=t1;int *td=t2; int m=26; int len=strlen(a); for(int i=0;i<m;i++) txt[i]=0; for(int i=0;i<len;i++){ rank[i]=a[i]-'a'; txt[a[i]-'a']++; }
for(int i=1;i<m;i++) txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--) sa[--(txt[a[i]-'a'])]=i;
//运用基数排序(计数排序)对于sa数组进行初始化sa; for(int k=1;k<=len;k*=2){ int p; p=0; for(int i=len-k;i<len;i++) td[p++]=i; for(int i=0;i<len;i++){ if(sa[i]>=k) td[p++]=sa[i]-k; }
//td数组表示的是第二关键字排名,详情见第二链接; for(int i=0;i<m;i++) txt[i]=0; for(int i=0;i<len;i++) txt[rank[td[i]]]++; for(int i=1;i<m;i++) txt[i]+=txt[i-1]; for(int i=len-1;i>=0;i--) sa[--txt[rank[td[i]]]]=td[i];
//依照第一关键词的排序,复合第二关键词的顺序,进行排序更新sa数组;
//更新完sa数组后 就对rank数组进行更新,因为rank数组可能存在字符串相等的情况 即rank数组下不同的i可能对应相同的值所以必须对其离散化 也就是判断是否会存在相同的字符串
//运用第一关键词和第二关键词的二元组来进行离散 swap(rank,td); rank[sa[0]]=0; p=0; for(int i=1;i<len;i++) rank[sa[i]]=cmp(td,sa[i-1],sa[i],k)?p:++p; if(p==len) return ; } return ; } void pd(char a[]){ int len=strlen(a); for(int i=0;i<len;i++) Rank[sa[i]]=i; } void hh(char a[]){ int len=strlen(a); h[sa[0]]=0; height[0]=0; for(int i=0;i<len;i++){ int xx=Rank[i]; int yy=sa[Rank[i]-1]; int flag=0; if(Rank[i]==0) continue; int p=h[i-1]-1; int w=i; if(p<0){ p=0; } else{ w+=p;yy+=p; } while(a[w]!='\0'&&a[yy]!='\0'){ if(a[w]==a[yy]) p++; else{ break; } w++;yy++; } h[i]=p; height[Rank[i]]=h[i]; }
//hh函数是求height数组 即在排序后后缀的与前一个后缀的公共前缀的字符数
//关键运用对于i满足,h[i]>=h[i-1]-1;具体证明可以百度 return ; } int main(){ scanf("%s",a); Sa(a); for(int i=0;i<strlen(a);i++) cout<<sa[i]<<" "; cout<<endl; cout<<"Rank:"<<endl; pd(a); for(int i=0;i<strlen(a);i++) cout<<Rank[i]+1<<" "; cout<<endl; cout<<"Height:"<<endl; hh(a); for(int i=0;i<strlen(a);i++) cout<<height[i]<<" "; cout<<endl; }
这算是这几天来的理解吧 在AC了一道模板题后 决定写下来这些心得 大致思路分析就是这样的还是需要多多刷题来巩固这些模板和思维
---- 眼界的宽度决定以后的成就