【集训第五天·后缀数组】哇哈哈哈哈~

  前面放了几天假(虽然我们是在补课),所以懒得写博客。补课期间学习了网络流,后面写专题总结。网络流主要是建模,慢慢搞。

  后缀数组这个东西非常难搞,当初老师讲的时候  我(lao)们(shi) 水了2h,讲完还是一脸懵逼。虽然思路是理解了,但是完全不知道代码是什么意思。cnm 

  然后某一天晚上我没交手机,坐在寝室的床上从23点啃代码啃到00:30,终于是明白了。。cnm

 

----------------------------------------------------------------------------分割线

  少年啊,结束你那无聊的吐槽,进入正题吧!

 

 

  好,说正事。

 

  计算和建立后缀数组的思路大致是这样的(倍增法):

  PS:排序时,如果关键字均相同,那么先出现的排在前面,即按照后缀开头字母在原串中的先后顺序排列。这样做的原因不明,我自己推测是可以后出现的排前面,但有2处这样的排序,必须保持一致

  1.把每位字符单个排序,排出第一次sa数组(基数排序,代码有点。。不想吐槽)

  2.枚举要计算的后缀长度,for(int k=1;k<=n;k<<1)此处就是倍增,先按照第二关键字排序(利用上一次求得的sa数组),再按照第一关键字排序(基数排序,桶桶桶),排出当前k长度下的sa数组

  这样就完了,是不是很简单??(智障)

  

  值得注意的是,代码中有两个优化

  1.当所有位置的排名都不同时,即至少有n个不同排名时,break掉循环。因为再往后找也没有什么卵子用啊,第一关键字才是排序重点啊,喂

  2.每次重新计算x数组,即元素数组(应该可以叫这个名字吧。。),表示不同的元素在前一次的排名(没啥卵用,只用知道它把相同的元素给揉到一块就行),这样可以优化m,即数组中元素个数

      对于思路的详细图解及正确性参考

      http://blog.csdn.net/yxuanwkeith/article/details/50636898

 

此处附上代码(网上的详解版,注释超级多)

CODE:

 

 1 void da(int *r,int *sa,int n,int m)
 2 {
 3     int i,k,p,*x=wa,*y=wb; //x数组相当于rank,y数组相当于第二关键字 
 4     for(i=0;i<m;i++) ws[i]=0; //m是字符的最大值,ws数组用于辅助基数排序
 5     for(i=0;i<n;i++) ws[x[i]=r[i]]++; //计算ws和x,x只用作比较排序,所以没有必要算出真实名次 
 6     for(i=1;i<m;i++) ws[i]+=ws[i-1]; //计算ws 
 7     for(i=n-1;i>=0;i--) sa[--ws[x[i]]]=i; 
 8     //计算第一次的sa值 0  2  1  3
 9    
10     for(k=1;k<=n;k<<=1) //倍增,k是当前串的长度 
11     {
12         p=0;
13         //对第二关键字进行排序,直接利用上一次计算出的sa数组 
14         for(i=n-k;i<n;i++) y[p++]=i; //空串肯定小,所以用的序表示sa[]
15         for(i=0;i<n;i++) if(sa[i]>=k) y[p++]=sa[i]-k; 
16         //上一次的sa左移k位还未消失,即sa[i]>=k,则按顺序写入y,得到第二关键字顺序  3  1  0  2 
17         
18         //对第一关键字进行排序 
19         for(i=0;i<m;i++) ws[i]=0; 
20         for(i=0;i<n;i++) ws[x[y[i]]]++; 
21         for(i=1;i<m;i++) ws[i]+=ws[i-1]; 
22         for(i=n-1;i>=0;i--) sa[--ws[x[y[i]]]]=y[i]; //0  2  3  1
23         
24         //因为下一次要用rank,所以必须要计算rank的值,最终存储在x中,y此时没用了,用来临时存储rank 
25         swap(x,y);//x,y为指针,所以赋值x的值到y,可直接交换指针的值
26         
27         //计算新的rank,因为可能有相同串,所以对于相同的rank必须相同,
28         //因为sa已经求出,则接利用求出的sa值来求,只需要找到哪些串相等,令其rank相同,不同的依次加1 
29         //方法是直接判断rank[i]和rank[i+k]的是否相同 
30         p=1,x[sa[0]]=0;
31         for(i=1;i<n;i++)
32             x[sa[i]]=   y[sa[i-1]==y[sa[i]]] && y[sa[i-1]+k]==y[sa[i]+k]  ? p-1:p++;
33         if(p>=n)break;//优化,如果名次全部不同,则完成 
34         m=p; //优化,字符最大为p,所以令m=p 
35     }
36 }
View Code

 

 

 

    应用:LCP

  

  然而在具体应用中(寻找LCP),用的是height数组,这东西有点绕,仔细理一下还是可以想清楚

  这里有一篇博客,关于这点写的非常清楚

  http://www.cnblogs.com/LLGemini/p/4771235.html

 

  有个很重要的东西就是h数组和height数组的定义,即h[i]=height[rank[i]],牢记这一点,因为height[i]不好直接计算,我们计算h[i]并通过上式求得height数组。

  为了快速求得h数组(height数组)我们引入一个定理 h[i]>=h[i-1]-1,证明略 

  有了这个定理,我们便可在O(n)的时间内计算出 h数组(height数组),因为每次计算新的h[i]时,用之前的h[i-1]-1,再看看后面是否有更多的重复即可,非常屌,搞得zj欲罢不能

 

 

 

  当然,今天除了看这个东西之外,我还刷了一些网络流的题,也在后面专题一起写吧。

  还有哦,后缀数组13个具体套路我还没有看,等我觉得把网络流学得差不多了就看吧。

 

  心情:过几天就要出去省选了,还有点小激动,又可以出去嗨一下了,也不知道我什么lgh最近不理我和zj两个人,到时后我们仨住三人间肯定会出现一些尴尬的情况,也只能随机应变了。大概可以让lgh和lence_ren睡双人间,我、zj、zbh一起浪。。。

posted @ 2017-04-05 21:42  _wsy  阅读(364)  评论(1编辑  收藏  举报