后缀数组

 后缀:Suffix(i)=r[i..len(r)]。

对于同一个字符串,两个开头位置不同的后缀 u 和 v 进行比较的结果不可能是相等,因为 u=v 的必要条件 len(u)=len(v) 在这里不可能满足。

后缀数组:后缀数组SA是一个一维数组,它保存1..n的某个排列 SA[1 ] ,SA[2] ,…… ,SA[n] ,并且保证 Suffix(SA[i]) < Suffix(SA[i+1]) ,1 ≤ i<n 。也就是将字符串S的n个后缀从小到大进行排序之后把排好序的后缀的开头位置顺次放入SA中。

名次数组:名次数组Rank[i]保存的是Suffix(i)在所有后缀中从小到大排列的“名次”。简单的说,后缀数组是 “排第几的是谁?”,名次数组是“你排第几?”。容易看出,后缀数组和名次数组为互逆运算。

设字符串的长度为n。为了方便比较大小,可以在字符串后面添加一个字 符,这个字符没有在前面的字符中出现过,而且比前面的字符都要小。
在求出名次数组后,可以仅用 O(1) 的时间比较任意两个后缀的大小。
在求出后缀数组或名次数组中的其中一个以后,便可以用 O(n) 的时间求出另外一个。
任意两个后缀如果直接比较大小,最多需要比较字符 n 次,也就是说最迟在比较第 n 个字符时 一定能分出 “ 胜负 ” 。
倍增算法
1.充分利用了各个后缀之间的联系,将构造后缀数组的最坏时间复杂度成功降至O(nlogn)。
 
 
View Code
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 const int Max = 20001;
 6 
 7 int  num[Max];
 8 int sa[Max], rank[Max], height[Max];
 9 int wa[Max], wb[Max], wv[Max], wd[Max];
10 int cmp(int *r, int a, int b, int l)
11 {
12     return r[a] == r[b] && r[a+l] == r[b+l];
13 }
14 
15 void da(int *r, int n, int m)
16 {          //  倍增算法 r为待匹配数组  n为总长度 m为字符范围
17     int i, j, p, *x = wa, *y = wb, *t;
18     for(i = 0; i < m; i ++) wd[i] = 0;
19     for(i = 0; i < n; i ++) wd[x[i]=r[i]] ++;
20     for(i = 1; i < m; i ++) wd[i] += wd[i-1];
21     for(i = n-1; i >= 0; i --) sa[-- wd[x[i]]] = i;
22     for(j = 1, p = 1; p < n; j *= 2, m = p)
23     {
24         for(p = 0, i = n-j; i < n; i ++) y[p ++] = i;
25         for(i = 0; i < n; i ++) if(sa[i] >= j) y[p ++] = sa[i] - j;
26         for(i = 0; i < n; i ++) wv[i] = x[y[i]];
27         for(i = 0; i < m; i ++) wd[i] = 0;
28         for(i = 0; i < n; i ++) wd[wv[i]] ++;
29         for(i = 1; i < m; i ++) wd[i] += wd[i-1];
30         for(i = n-1; i >= 0; i --) sa[-- wd[wv[i]]] = y[i];
31         for(t = x, x = y, y = t, p = 1, x[sa[0]] = 0, i = 1; i < n; i ++)
32             x[sa[i]] = cmp(y, sa[i-1], sa[i], j) ? p - 1: p ++;
33     }
34 }
35 
36 void calHeight(int *r, int n)
37 {           //  求height数组。
38     int i, j, k = 0;
39     for(i = 1; i <= n; i ++) rank[sa[i]] = i;
40     for(i = 0; i < n; height[rank[i ++]] = k)
41         for(k ? k -- : 0, j = sa[rank[i]-1]; r[i+k] == r[j+k]; k ++);
42 }
43 
44 int main()
45 {
46     char str[Max];
47     int i, m=30, len;
48     while(scanf("%s",str)!=EOF)
49     {
50         len=strlen(str);
51         for(i=0;i<=len;i++)num[i]=str[i]-'a'+1;
52         num[len]=0;
53         da(num, len + 1, m);
54         calHeight(num, len);
55         printf("num: \n");
56         for(i=0;i<=len;i++){printf("%d ",num[i]);}printf("\n");
57         printf("sa: \n");
58         for(i=0;i<=len;i++){printf("%d ",sa[i]);}printf("\n");
59         printf("rank: \n");
60         for(i=0;i<=len;i++){printf("%d ",rank[i]);}printf("\n");
61         printf("height: \n");
62         for(i=0;i<=len;i++){printf("%d ",height[i]);}printf("\n");
63     }
64     return 0;
65 }
66 
67 /*
68 ababababa
69 */
70 
71 /*
72 num[0~n-1]为有效值 就是输入的字符串稍稍转化而成的数组
73 sa[1~~n]为有效值  sa[i]=a则代表排在第 i 位的是第a个后缀。  a属于[0~~n-1]
74 rank[0~~n-1]是有效值  rank[i]=b则代表第 i 个后缀排在第b位   b属于[1~~n]
75 height[2~~n]是有效值  height[i]=c 则代表排在第 i 位的后缀和排在第i-1的后缀的最长前缀长度是c
76 例如:
77 sa:
78 8 a
79 6 aba
80 4 ababa
81 2 abababa
82 0 ababababa
83 7 ba
84 5 baba
85 3 bababa
86 1 babababa
87 得sa: 9 8 6 4 2 0 7 5 3 1
88 即将所有的后缀排序,排在第0位的是空串,第一位的是a,....
89 rank: 5 9 4 8 3 7 2 6 1 0
90 即sa=0的后缀排在sa数组中的第5位,即最长的后缀排在第五位,...
91 height 0 0 1 3 5 7 0 2 4 6
92 rank和height都是针对sa说的。
93 */

这个算法看的还是不太明白啊。

posted @ 2012-08-20 16:57  pushing my way  阅读(286)  评论(0编辑  收藏  举报