后缀数组
后缀: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 */
这个算法看的还是不太明白啊。