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.
    如果你生活在回声里,
    你的心跳声永远不会轰鸣作响。
  • 相关阅读:
    微信浏览器取消缓存的方法
    iphone safari浏览器CSS兼容性的解决方案集合
    配置iis支持.json格式的文件
    win7下使用IIS服务器及自定义服务器端包含模块(SSI)步骤
    前端组件库集合
    ClientValidationFunction
    java 查询solr时间格式
    为何大量网站不能抓取?爬虫突破封禁的6种常见方法
    反爬虫四个基本策略
    ScheduledExecutorService 定时器用法
  • 原文地址:https://www.cnblogs.com/wsy01/p/6670768.html
Copyright © 2011-2022 走看看