后缀数组——附1(含模板)

http://blog.csdn.net/jokes000/article/details/7839686

后缀数组sa:将s的n个后缀从小到大排序后将 排序后的后缀的开头位置 顺次放入sa中,则sa[i]储存的是排第i大的后缀的开头位置。简单的记忆就是“排第几的是谁”。

名次数组rank:rank[i]保存的是suffix(i){后缀}在所有后缀中从小到大排列的名次。则 若 sa[i]=j,则 rank[j]=i。简单的记忆就是“你排第几”。

对于 后缀数组sa 与 名次数组rank ,有rank[ sa[i] ]=i (这是很重要的一点,通过sa与rank的关系可以求出后缀数组)

由此可看出,后缀数组sa 与名次数组rank的关系为互逆关系。

求出了rank和sa数组还不够,通常我们需要由rank与sa数组计算出一个辅助工具height数组——最长公共前缀(LCP)。

height 数组: 定义height[i]=suffix(sa[i-1]) 和 suffix(sa[i]) 的最长公共前缀,也就是排名相邻的两个后缀的最长公共前缀。

 

             

             字符串aabaaaab的sa数组与rank数组                                                                                                          倍增算法的计算过程

 

              计算后缀"abaaaab"和后缀"aaab"的最长公共前缀

 

 

 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 #define maxn  110010*2
 6 
 7 #define cls(x) memset(x, 0, sizeof(x))
 8 
 9 int wa[maxn],wb[maxn],wv[maxn],wss[maxn];
10 
11 int cmp(int *r,int a,int b,int l){return r[a]==r[b]&&r[a+l]==r[b+l];}
12 
13 //倍增算法
14 void da(char *r,int *sa,int n,int m)
15 {
16      cls(wa);
17      cls(wb);
18      cls(wv);
19      cls(wss);
20      int i,j,p,*x=wa,*y=wb,*t;
21      //基数排序
22      for(i=0;i<m;i++) wss[i]=0;
23      for(i=0;i<n;i++) wss[x[i]=r[i]]++;
24      for(i=1;i<m;i++) wss[i]+=wss[i-1];
25      for(i=n-1;i>=0;i--) sa[--wss[x[i]]]=i;
26 
27      // 在第一次排序以后,rank数组中的最大值小于p,所以让m=p。整个倍增算法基本写好,代码大约25行。
28      for(j=1,p=1;p<n;j*=2,m=p)
29      {
30        //接下来进行若干次基数排序,在实现的时候,这里有一个小优化。基数排序要分两次,第一次是对第二关键字排序,第二次是对第一关键字排序。对第二关键字排序的结果实际上可以利用上一次求得的sa直接算出,没有必要再算一次
31        for(p=0,i=n-j;i<n;i++) y[p++]=i;
32        for(i=0;i<n;i++) if(sa[i]>=j) y[p++]=sa[i]-j;
33 
34        //其中变量j是当前字符串的长度,数组y保存的是对第二关键字排序的结果。然后要对第一关键字进行排序,
35        for(i=0;i<n;i++) wv[i]=x[y[i]];
36        for(i=0;i<m;i++) wss[i]=0;
37        for(i=0;i<n;i++) wss[wv[i]]++;
38        for(i=1;i<m;i++) wss[i]+=wss[i-1];
39        for(i=n-1;i>=0;i--) sa[--wss[wv[i]]]=y[i];
40 
41        //这样便求出了新的sa值。在求出sa后,下一步是计算rank值。
42        for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i<n;i++)
43        x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;
44      }
45 }
46 
47 int rank[maxn],height[maxn];
48 
49 //得到height数组:排名相邻的两个后缀的最长公共前缀
50 void calheight(char *r,int *sa,int n)
51 {
52      cls(rank);
53      cls(height);
54      int i,j,k=0;
55      for(i=1;i<n;i++) rank[sa[i]]=i;
56      for(i=0;i<n;height[rank[i++]]=k)
57      for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);
58      return;
59 }
60     
61 char ca[maxn];
62 int sa[maxn];
63 
64 int main()
65 {
66     while (cin >> ca)
67     {
68         int len = strlen(ca);
69         int lenstr1 = len;
70         ca[len] = '#';
71         cin >> (ca + len + 1);
72         len = strlen(ca);
73         da(ca, sa, len, 130);
74 
75         int i;
76         calheight(ca,sa,len);
77 
78         int max = 0;
79         for (i = 1; i < len; ++i)
80         {
81             if (height[i] > max)
82             {
83                 if ((sa[i] > lenstr1 && sa[i - 1] < lenstr1) || (sa[i - 1] > lenstr1 && sa[i] < lenstr1))
84                 {
85                     max = height[i];
86                 }
87             }
88         }
89         cout << max << endl;
90         cls(ca);
91     }
92     return 0;
93 }

 

 

 

posted @ 2013-07-18 21:11  crazy_apple  阅读(991)  评论(0编辑  收藏  举报