需要说的都已经在源代码中注明了!

 

 
  1. #include<stdio.h> 
  2. #include<string.h> 
  3. #include<algorithm> 
  4. using namespace std; 
  5. int cmp(int *r,int a,int b,int l){ 
  6.     return r[a]==r[b]&&r[a+l]==r[b+l]; 
  7. #define M 100 
  8. int wa[M],wb[M],wv[M],ws[M]; 
  9. int SA[M]; 
  10.  
  11. char s[M]=“aabaaaab”
  12.  
  13. void print(char *label,int *arr,int n){ 
  14.     puts(label); 
  15.     for(int i=0;i<n;i++) 
  16.         printf(“%d “,arr[i]); 
  17.     puts(“”); 
  18. void da(char *r,int *sa,int n,int m){ 
  19.      int i, j, p, *x = wa, *y = wb; 
  20.      for (i = 0; i < m; i++) 
  21.          ws[i] = 0; 
  22.      for (i = 0; i < n; i++) 
  23.          ws[x[i] = r[i]]++; 
  24.      for (i = 1; i < m; i++) 
  25.          ws[i] += ws[i – 1]; 
  26.      for (i = n – 1; i >= 0; i–) 
  27.          sa[–ws[x[i]]] = i; 
  28.      print(“array sa:”,sa,n); //**************print************** 
  29.  
  30.  
  31.      for (j = 1, p = 1; p < n; j *= 2,m=p) 
  32.      { 
  33.          //对第二关键字进行排序:由上一次的结果 直接算出来 
  34.          for (p = 0, i = n – j; i < n; i++) y[p++] = i; 
  35.          /* 
  36.           * y [p++]  表示已经移出表达的范围外面了,所以一定是最小的, 
  37.           * 排在前面,但由于我们实际振作的是下标,所以y存储的也是下标, 
  38.           * 而且第一个出界的是n-j.实际上排名不分先后 
  39.           * 
  40.           * 下面的sa[i]-j  即对已经排好序的元素,进行降级处理,表示 
  41.           * 及向后移动了j位,实际上这就是后缀的第二关键字  由基数排序 
  42.           * 的特点,如果第二关键字有序了,那么排到第一关键字有序的时候, 
  43.           * 整个序列就有序了这里我还没有相通的一点是 if(sa[i]>=j)sa[i]-j 
  44.           * 这样做了以后,会不会有重复的值出现呢? 其实不会,这里它把 
  45.           * sa[i]值<j的过滤了  我们一定要记住sa[i]储存的是下标,sa[i]小于j,实际 
  46.           *  上是最前面的数。 这就有点像滑动窗口的感觉 ! 指定窗口的大小 
  47.           *  为s.size(),窗口以倍增的方式向后滑动,滑动窗口方向 前面 越界 
  48.           *  的数值设为0,滑动窗口后面越界的数值扔掉。这样就保证有序了! 
  49.           * 
  50.           *  好吧,这段代码花了我几天的时间,如果今天不回头看看 基数 
  51.           *  排序,可能还给我几天都搞不懂的! 唉!基础是硬伤啊!!! 
  52.           * */ 
  53.          for (i = 0; i < n; i++) 
  54.              if (sa[i] >= j) y[p++] = sa[i] – j; 
  55.          print(“array y:”,y,n); //**************print************** 
  56.  
  57.          //对第一关键字进行排序 
  58.          for (i = 0; i < n; i++) wv[i] = x[y[i]]; 
  59.          print(“wv:”,wv,n);//************************************** 
  60.          /* 
  61.           * 如果前面理解了,wv[i]=x[y[i]]也就好理解了 
  62.           *实际上:论文里面已经说得很清楚了:x[i]相当于上次排序的rank数组, 
  63.               ,而我们的y相当于局部SA数组(只考虑到了一个关键字) 
  64.           *如果SA[i]=k,那么一定有 rank[k]=i, 这样的话,这段代码就相当于 rank[sa[i]]=k, 
  65.           *就 相当于把数据从小到大扔到待排序的桶里去(联系基数排序来理解 ) 
  66.           * */ 
  67.         /* 
  68.          * 这下面的几行与第一次排序几乎是一模一样的,没有什么好说的 
  69.          * 
  70.          * */ 
  71.          for (i = 0; i < m; i++) ws[i] = 0; 
  72.          for (i = 0; i < n; i++) ws[wv[i]]++; 
  73.          for (i = 1; i < m; i++) ws[i] += ws[i – 1]; 
  74.          for (i = n – 1; i >= 0; i–) sa[–ws[ wv[i]]] = y[i]; 
  75.  
  76.  
  77.          /* 
  78.           * 完成排序,通过排序的结果求新的排名 
  79.           *  swap(x, y) 这里就不怎么好理解  x[i]相当于上次排序的rank数组, 
  80.           *  而我们的y相当于局部SA数组(只考虑到了一个关键字) 
  81.           *  交换过后,x就变成局部SA数组了,而y就变成了rank数组 
  82.           *   为了思路连贯,方便理解,我把代码调整一下*/ 
  83.           //         for (  swap(x, y),p = 1, x[sa[0]] = 0, i = 1; i < n; i++) 
  84. //           x[sa[i]] = cmp(y, sa[i – 1], sa[i], j) ? p – 1 : p++; 
  85.          for ( p = 1, y[sa[0]] = 0, i = 1; i < n; i++) 
  86.             y[sa[i]] = cmp(x, sa[i – 1], sa[i], j) ? p – 1 : p++; 
  87.  
  88.          swap(x, y); 
  89.          /* 
  90.           *  这样的话,个人觉得好理解多了! 
  91.           *  首先这几行代码的作用是:通过SA来计算新的rank值 
  92.           *   cmp(x, sa[i – 1], sa[i], j) ? p – 1 : p++; 
  93.           *   sa[i-1],sa[i]就表示排序后两个相邻的后缀的首地址 
  94.           * 
  95.           *   x[sa[i-1] ,x[sa[i]]就表示两个排名相邻的元素 
  96.           *   其实我个人觉得 x[]数组 既表示了排名,又兼顾表示的原字符串 
  97.           *   这里比较抽象,不好理解 
  98.           * */ 
  99.  
  100.      } 
  101. int main(){ 
  102.     int n=strlen(s); 
  103.     int m=128; 
  104.     da(s,SA,n+1,m); 
  105.     print(“sa:”,SA,n+1);//**************print************** 
  106.  
  107.     return 0; 
  108. /* 
  109.  * sa: 
  110.    8 3 4 5 0 6 1 7 2 
  111.  * */