这几天弄后缀数组,真的是让人纠结啊!头都大了!网上也很难找到详细点的资料。不得不感叹,智商是硬伤啊!

       不过还好,弄这两天,总算有些收获。我研究的一直是《后缀数组 —— 处理字符串的有力工具》这篇论文。看了很久,对于其实现,一直是云里雾里!

        后来在网上找了一段实现rank数组和sa数组的源程序。慢慢知道了一些实现的过程。可是做到poj2774这道题的时候,又是RE了!唉……到现在都还没有解决。先把收获到的东西整理整理吧!希望能起到温故而知新的作用。

       先就说里面的倍增算法的实现吧!

   

   
   
   
   
  1.  int wa[maxn],wb[maxn],wv[maxn],ws[maxn];  
  2. int cmp(int *r,int a,int b,int l)  
  3. {return r[a]==r[b]&&r[a+l]==r[b+l];}  
  4. void da(int *r,int *sa,int n,int m)  
  5. {  
  6. int i,j,p,*x=wa,*y=wb,*t;  
  7. for(i=0;i
  8. for(i=0;i
  9. for(i=1;i
  10. for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i;  
  11. for(j=1,p=1;p
  12. {  
  13. for(p=0,i=n-j;i
  14. for(i=0;iif(sa[i]>=j) y[p++]=sa[i]-j;  
  15. for(i=0;i
  16. for(i=0;i
  17. for(i=0;i
  18. for(i=1;i
  19. for(i=n-1;i>=0;i--) sa[--ws[wv[i]]]=y[i];  
  20. for(t=x,x=y,y=t,p=1,x[sa[0]]=0,i=1;i
  21. x[sa[i]]=cmp(y,sa[i-1],sa[i],j)?p-1:p++;  
  22. }  
  23. return;  

这段代码我开始根本连入口都不懂。里面的细节就更不用说了!

论文里面说void da(int *r,int *sa,int n,int m)的数组r存放的是源字符串

比如:对aabaaaab这个字符串。可是它却是int类型的,这是纠结我第一点的地方。

后来,看到网上的源代码,才明白,r数组是这样实现的:  

int up = 0;
  for (int i = 0; i < string.length(); i++) {
   r[i] = string.charAt(i) - 'a';// 纯字母字符串
   if (up < r[i])
    up = r[i];
  }
  up++;

原来存入的是ASCII码值。

第二点不明白的是:int n,int m; n是应该是字符串的长度,那m是什么呢?

其实m就是上面代码中的up。保证m大于r中最大的那个值就可以了!

于是呢对于''aabaaaab'就可以得到sa和rank了!

到后面需要计算height数组了!我又晕乎了……

于是又开始慢慢慢慢慢慢地去研究!

   
   
   
   
  1. int rank[maxn],height[maxn];  
  2. void calheight(int *r,int *sa,int n)  
  3. {  
  4. int i,j,k=0;  
  5. for(i=1;i<=n;i++) rank[sa[i]]=i;  
  6. for(i=0;i
  7. for(k?k--:0,j=sa[rank[i]-1];r[i+k]==r[j+k];k++);  
  8. return;  

我建议不明白这段代码的读者通过对概念的理解用aabaaaab自己手工实现一遍height数组。或许这样就可以明白了。至少我是这样做的!

也许这段代码可以帮助大家更好的理解上面的代码:

 

   
   
   
   
  1. public static int[] calheight(int[] r, int[] sa, int[] rank, int n) {  
  2.         int height[] = new int[n + 1];  
  3.         int i = 0, j = 0, k = 0;  
  4.         for (i = 0; i < n; i++) {  
  5.             // 初始化  
  6.             if (k != 0)  
  7.                 k--;  
  8.             if (rank[i] - 1 >= 0) {  
  9.                 j = sa[rank[i] - 1];  
  10.             } else {  
  11.                 j = 0;  
  12.             }  
  13.             while (true) {  
  14.                 if (j + k >= n || i + k >= n)  
  15.                     break;  
  16.                 if (r[i + k] != r[j + k])  
  17.                     break;  
  18.                 k++;  
  19.             }  
  20.             height[rank[i]] = k;  
  21.         }  
  22.         return height;  
  23.     } 

其实就是定位到两个后缀上,然后呢比较r数组中的值来确定最长公共部分是多少:

r[i + k] == r[j + k] i是一个后缀的起点,j是排名(请注意,这里是排名)在i后面的后缀的起点。而k代表了公共部分的长度。

而且呢这里height数组也是通过hi数组间接计算的height[rank[i]] = k;

而实际上height[rank[i]] 就是hi[i].(博客首提到了那篇论文里有详细的解释)

由于我自己也没有理解清楚,就先总结到这里吧。把自己的代码贴出来吧:

 

   
   
   
   
  1. package com.xh.SA;  
  2.  
  3. public class SA05 {  
  4.     static int[] wa = new int[100];  
  5.     static int[] wb = new int[100];  
  6.     static int[] wv = new int[100];  
  7.     static int[] ws = new int[100];  
  8.  
  9.     public static boolean cmp(int[] r, int a, int b, int l) {  
  10.         return r[a] == r[b] && r[a + l] == r[b + l];  
  11.     }  
  12.  
  13.     public static int[] da(int[] r, int[] sa, int n, int m) {  
  14.         int i, j, p;  
  15.         int[] x = wa;  
  16.         int[] y = wb;  
  17.         int[] t = new int[10];  
  18.         for (i = 0; i < m; i++)  
  19.             ws[i] = 0;  
  20.         for (i = 0; i < n; i++)  
  21.             ws[x[i] = r[i]]++;  
  22.         for (i = 1; i < m; i++)  
  23.             ws[i] += ws[i - 1];  
  24.         for (i = n - 1; i >= 0; i--)  
  25.             sa[--ws[x[i]]] = i;  
  26.         for (j = 1, p = 1; p < n; j *= 2, m = p) {  
  27.             for (p = 0, i = n - j; i < n; i++)  
  28.                 y[p++] = i;  
  29.             for (i = 0; i < n; i++)  
  30.                 if (sa[i] >= j)  
  31.                     y[p++] = sa[i] - j;  
  32.             for (i = 0; i < n; i++)  
  33.                 wv[i] = x[y[i]];  
  34.             for (i = 0; i < m; i++)  
  35.                 ws[i] = 0;  
  36.             for (i = 0; i < n; i++)  
  37.                 ws[wv[i]]++;  
  38.             for (i = 1; i < m; i++)  
  39.                 ws[i] += ws[i - 1];  
  40.             for (i = n - 1; i >= 0; i--)  
  41.                 sa[--ws[wv[i]]] = y[i];  
  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.         return sa;  
  46.     }  
  47.  
  48.     public static int[] calheight(int[] r, int[] sa, int[] rank, int n) {  
  49.         int height[] = new int[n + 1];  
  50.         int i = 0, j = 0, k = 0;  
  51.         for (i = 0; i < n; i++) {  
  52.             // 初始化  
  53.             if (k != 0)  
  54.                 k--;  
  55.             if (rank[i] - 1 >= 0) {  
  56.                 j = sa[rank[i] - 1];  
  57.             } else {  
  58.                 j = 0;  
  59.             }  
  60.             while (true) {  
  61.                 if (j + k >= n || i + k >= n)  
  62.                     break;  
  63.                 if (r[i + k] != r[j + k])  
  64.                     break;  
  65.                 k++;  
  66.             }  
  67.             height[rank[i]] = k;  
  68.         }  
  69.         return height;  
  70.     }  
  71.  
  72.     public static void main(String[] args) {  
  73.         String string = "aabaaaab";  
  74.         int[] r = new int[string.length()];  
  75.         int[] sa = new int[string.length()];  
  76.         int[] rank = new int[string.length()];  
  77.         int[] height = new int[string.length()];  
  78.         int up = 0;  
  79.         for (int i = 0; i < string.length(); i++) {  
  80.             r[i] = string.charAt(i) - 'a';// 纯字母字符串  
  81.             if (up < r[i])  
  82.                 up = r[i];  
  83.         }  
  84.         up++;  
  85.         // r[string.length()]=up;//末尾与所有字符不同,用于height数组的计算  
  86.         sa = da(r, sa, string.length(), up);  
  87.         for (int i = 0; i < sa.length; i++) {  
  88.             rank[sa[i]] = i;//通过rank和sa的关系求出rank的值  
  89.         }  
  90.         // 到这里rank和sa都已经得到了  
  91.         // 下面需要做的就是求height的值了  
  92.         System.out.println("sa数组:");  
  93.         for (int i = 0; i < sa.length; i++) {  
  94.  
  95.             System.out.print(sa[i] + ",");  
  96.         }// 下面需要做的就是求height的值了  
  97.         System.out.println("\nrank数组:");  
  98.         for (int i = 0; i < sa.length; i++) {  
  99.  
  100.             System.out.print(rank[i] + ",");  
  101.         }  
  102.         height = calheight(r, sa, rank, string.length());  
  103.         System.out.println("\nheight数组:");  
  104.         for (int i = 1; i < sa.length; i++) {  
  105.              //我觉得按照定义,height[0]应该就是为0吧……  
  106.             System.out.print(height[i] + ",");  
  107.         }  
  108.         /*注:由于我的程序是从0位开始的,故比《算法合集之《后缀数组——处理字符串的有力工具》.pdf》中的结果都  
  109.          * 小1 */ 
  110.     }  
  111. }  

先就到这里吧!下去再仔细研究研究!希望能弄懂,把整个实现的细节清楚的记录下来!