zoukankan      html  css  js  c++  java
  • 【集训第五天·后缀数组】哇哈哈哈哈~

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

      后缀数组这个东西非常难搞,当初老师讲的时候  我(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一起浪。。。










    If you live in the echo,
    your heart never beats as loud.
    如果你生活在回声里,
    你的心跳声永远不会轰鸣作响。
  • 相关阅读:
    一些你可能用到的代码
    iOS 键盘下去的方法
    iOS设计模式汇总
    随笔
    Spring cloud config 分布式配置中心 (三) 总结
    Spring cloud config 分布式配置中心(二) 客户端
    Spring cloud config 分布式配置中心(一) 服务端
    jdbcUrl is required with driverClassName spring boot 2.0版本
    JpaRepository接口找不到 spring boot 项目
    解决IntelliJ “Initialization failed for 'https://start.spring.io'
  • 原文地址:https://www.cnblogs.com/wsy01/p/6670768.html
Copyright © 2011-2022 走看看