码迷,mamicode.com
首页 > 编程语言 > 详细

关于后缀数组的一点想法

时间:2017-06-02 22:07:16      阅读:203      评论:0      收藏:0      [点我收藏+]

标签:一些事   公共前缀   img   完美解决   .com   定义   注意事项   包括   []   

  后缀数组大概就是用后缀排名来搞一些事情,因为字符串中的每一个子串都可看做某一后缀的前缀

  可用倍增法求出后缀排名

 

 

一、数组意义(对于字符串 s)

  sa[i]:排名为i的后缀的开头在s中的位置

  height[i]:排名为i的后缀和排名为i-1的后缀的LCP(最长公共前缀)

  c[]:用于基数排序,统计前缀和

  rank[i]:以s[i]开头的后缀的排名  显然 rank[sa[i]]=i    sa[rank[i]]=i

 

二、求sa[]具体思路

  1.用倍增法构造第一、第二关键词,第一关键词小的排在前,第一关键词相同的 第二关键词小的排在前。

  2.优化:如果后缀长度枚举到某一大小时,每个后缀的排名彼此不同,那么可以直接退出,道理很显然

 

三、求height[]具体思路

  先求出rank[i]

  看height[]的定义,知道应取suffix(sa[rank[i-1]])和suffix(i)的LCP

  只要找到suffix(sa[rank[i-1]])的开头j,暴力枚举便可

  此处有一个小优化(详情参见http://www.cnblogs.com/LLGemini/p/4771235.html

     h[]即为height[]

   对于i>1 且Rank[i]>1,一定有h[i]≥h[i-1]-1。(这条性质要好好理解!)

 

   证明:设suffix(k)是排在suffix(i-1)前一名的后缀,它们的最长公共前缀是h[i-1]。

 

              那么suffix(k+1)将排在suffix(i)的前面(这里要求h[i-1]>1,如果h[i-1]≤1,原式显然成立)并且suffix(k+1)和suffix(i)的最长公共前缀是h[i-1]-1,

 

              所以suffix(i)和在它前一名的后缀的最长公共前缀至少是h[i-1]-1。

 

              按照h[1],h[2],……,h[n]的顺序计算,并利用h 数组的性质,时间复杂度可以降为O(n)。

 

四、注意事项

  构建sa[]时,传4个参进入函数,设原字符串为s,设s的长度为n,s中最大字符的大小为m

  build_sa(s[],sa[],n+1,m)

  传n+1而不传n的原因是在s的末尾补上了一个 “0”

  原因:防止数组越界

     for(int i=1;i<n;i++)x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;

 

    如果 x[idx1]==x[idx2](注意idx1!=idx2), 说明以 idx1或 idx2 开头的长度为 len 的字符串肯定不包括字符 x[n-1] , 所以调用变量 sa[idx1+len] 和 sa[idx2+len] 不会导致数组越界, 这样就不需要做特殊判断.

  

  完美解决了!

代码:  

技术分享
 1 int sa[N],rk[N],h[N],c[N],r[N],wa[N],wb[N],sp[N],n,k; 
 2 
 3 void get_sa(int *r,int *sa,int n,int m){
 4     int *x=wa,*y=wb;//都是辅助变量 
 5     for(int i=0;i<n;i++)c[x[i]=r[i]]++;
 6     for(int i=1;i<m;i++)c[i]+=c[i-1];
 7     for(int i=n-1;i>=0;i--)sa[--c[x[i]]]=i;
 8     for(int k=1;k<=n;k<<=1){
 9         int p=0;
10         for(int i=n-k;i<n;i++)y[p++]=i;
11         for(int i=0;i<n;i++)if(sa[i]>=k)y[p++]=sa[i]-k;
12         
13         for(int i=0;i<m;i++)c[i]=0;
14         for(int i=0;i<n;i++)c[x[i]]++;
15         for(int i=1;i<m;i++)c[i]+=c[i-1];
16         for(int i=n-1;~i;i--)sa[--c[x[y[i]]]]=y[i];
17         
18         swap(x,y);//x,y是指针,直接互换 
19         p=1;x[sa[0]]=0;
20         for(int i=1;i<n;i++)x[sa[i]]= y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k]?p-1:p++;
21         if(p>=n)break;
22         m=p;//优化:最多有p个元素,下一次最大值为p 
23     }
24 }
25 
26 void get_h(){
27     int k=0,mh=-1;
28     for(int i=0;i<n;i++)rk[sa[i]]=i;
29     for(int i=0;i<n;i++){
30         if(k)k--;
31         int j=sa[rk[i]-1];
32         while(r[i+k]==r[j+k])k++;
33         h[rk[i]]=k; 
34     }
35 }
View Code

 

  

 

 

  2017-06-02 20:58:43

 

关于后缀数组的一点想法

标签:一些事   公共前缀   img   完美解决   .com   定义   注意事项   包括   []   

原文地址:http://www.cnblogs.com/wsy01/p/6935135.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!