现在的位置: 首页 > 综合 > 正文

后缀数组学习笔记(一)

2012年01月24日 ⁄ 综合 ⁄ 共 2336字 ⁄ 字号 评论关闭

 

   研究了一个星期的后缀数组,过程很痛苦,但还是有很多收获,付出更收获真的成正比的。发现后缀数组的强大功能,需慢慢领会。
后缀数组可以代替后缀树巧妙的解决大多数字符串问题,其强大功能主要有三个数组:
   1.suff[i],存放字符串S生成的后缀数组中排在第i位置,也就是将的n个后缀从小到大排序后把排好序的后缀的开头位置顺次放入到suff中。
   2.rank[i],rank数组跟suff[i]相对应,suff[i]是排第i的在哪,rank[i]表示第i个开始的字串排第几,即rank[suff[i]]=i
   3.height[i],height加强了后缀数组的威力,height[i]表示排名第i的后缀与排名第i-1的后缀的最长公共前缀。
LCP(最长公共前缀),LCP(i,j)表示后缀数组中第i个和第第j个后缀的最长公共前缀的长度,LCP定理:
LCP[i,j]=min{Height[k]|i+1=<k<=j};可以用RMQ来求区间内的最小值。
   用2倍增算法可以在O(nlogn)时间内求出suff,rank数组。这个看了很长时间,具体实现:
 
 suff,rank的实现: 
   void CreatSuff(int m)
   {
     
       int i,h,p;
       //用计数排序求出长度为1的suff值
       for(i=0;i<m;i++)
           C[i]=0;
       for(i=0;i<len;i++)
           C[x[i]=r[i]]++;
       for(i=1;i<m;i++)
           C[i]+=C[i-1];
       for(i=len-1;i>=0;i--)
               suff[--C[x[i]]]=i;   // 其中x存放rank的值,以上是计数排序模块
       //根据suff求出rank,再利用长度为h的rank为关键字,基数排序,求出长度为2*h的suff.
       for(h=1,p=0;p<len;h<<=1,m=p)
       {
             for(p=0,i=len-h;i<len;i++)
                y[p++]=i;        //y存放第二关键字排序的结果,这里有点技巧,你用上次的suff对第二关键子排序
             for(i=0;i<len;i++)
                if(suff[i]>=h)
                   y[p++]=suff[i]-h;
             for(i=0;i<len;i++)              //对第一关键字进行计数排序 
                  wv[i]=x[y[i]];
             for(i=0;i<m;i++)
                  C[i]=0;   
             for(i=0;i<len;i++)
                  C[wv[i]]++;
             for(i=1;i<m;i++)
                  C[i]+=C[i-1];
             for(i=len-1;i>=0;i--)
                  suff[--C[wv[i]]]=y[i];         
             t=x;x=y;y=t;p=1;                  //利用suff求rank,变量p其实是不同字符串的个数,这里让
m=p,当p=len时就结束
             for(x[suff[0]]=0,i=1;i<len;i++)
                  x[suff[i]]=cmp(y,suff[i],suff[i-1],h) ? p-1 : p++;
       }
   
   }     
   int cmp(int *v,int a,int b,int h)
   {
      return  v[a]==v[b]&&v[a+h]==v[b+h] ;   //这里要满足从a和b开始长度为h的前缀相同以及从a+h和b+h开始长度为h的前缀相同,
这样才能判定长度为2h的前缀相同
   }     
      
Height的实现:求Height时要用到一个定理,令H[i]=Heigth[rank[i]],也就是以第i个开始的后缀与在后缀数组中排在他前一位后缀的公共前缀长度
利用这个定理H[i]>=H[i-1]-1可以在O(n)内求出Height,用归纳法很容易证明。
  void CalHeight()
  {
       int k,i,p;
       for(k=0,p=0;p<len;p++)
       {
              if(x[p]==0)
                 Height[x[p]]=0;            //规定Height[0]=0
               else
               {
                     for(i=suff[x[p]-1];r[i+k]==r[p+k];k++);   //i为在后缀数组中排在p的前一位,从两个串第k个开始比较,因为前k-1个已相等     
                        Height[x[p]]=k;
                     if(k>0)
                        k--;
               }
       }
   
  }
后缀数组的应用十分灵活,继续学习中!

 

抱歉!评论已关闭.