zoukankan      html  css  js  c++  java
  • 后缀数组-倍增法

    额,写的有点混乱,改天整理一下

    链接:https://hihocoder.com/problemset/problem/1403

     1 //字符串从1开始
     2 //rank[i]为字符串中i位置起的后缀排序后的顺序
     3 //sa[i]为排序后的第i位置对应的后缀在字符串中的起始位置
     4 //sa[rank[i]]=i此公式在最终结果才成立
     5 //height[i]为排序数组中i位置与i-1位置的最长公共前缀的长度
     6 //H[i]为字符串i位置起的后缀与其在排序后前面相邻位置处的后缀的最大公共前缀
     7 //H[i]=height[rank[i]]此公式在最终结果时才成立(因为过程中rank[i]可能会与rank[j]重复)
     8 void solve()
     9 {
    10     for(int i=0;i<256;i++) cntA[i]=0;
    11     for(int i=1;i<=n;i++) cntA[ch[i]]++;
    12     //字符产从1开始,统计每个字符出现个数
    13     for(int i=1;i<256;i++) cntA[i]+=cntA[i-1];
    14     //统计该字符前出现字符的个数
    15     for(int i=n;i;i--) sa[cntA[ch[i]]--] = i;
    16     //至此单个字符排序的sa数组构成
    17     rank[sa[1] ] = 1;
    18     //根据sa数组顺序,开始求rank数组
    19     for(int i=2;i<=n;i++)
    20     {
    21         rank[sa[i]] = rank[sa[i-1]];
    22         //sa相邻字符相同时rank值相同
    23         if(ch[sa[i]]!=ch[sa[i-1]])
    24             rank[sa[i]]++;
    25         //相邻字符不相同时rank值增加1
    26     }
    27     //后续排序的对象是之前初次排序的次序值,并非真实值
    28     for(int l=1;rank[sa[n]]<n;l<<=1)//l为已有中间结果的长度
    29     {//结束条件:应该可以换为l<n,当l>=n时结束,求出的是各后缀数组的rank和sa
    30      //采用当前这种形式,是考虑到l不必扩展到n即可得出最终结果的情况
    31      //rank[sa[n]]<n表明存在rank[i]==rank[j]的情况,此时还需继续计算
    32      //最终的结果中是不会出现重复rank的情况
    33      //而rank[sa[n]]>=n,则表示不存在重复rank,即可代表后缀排序,此时已经可以作为最终结果
    34         for(int i=0;i<=n;i++) cntA[i] = 0;
    35         for(int i=0;i<=n;i++) cntB[i] = 0;
    36         for(int i=1;i<=n;i++)
    37         {
    38             //到n截止是因为字符串长n
    39             //排序后最后序值不会过n
    40             cntA[A[i] = rank[i]]++;
    41             //A数组记录i位置处第一关键字值即排序
    42             //cntA数组记录各第一关键字出现次数
    43             cntB[B[i]=(i+l<=n)?rank[i+l]:0 ]++;
    44             //B数组记录i位置处第二关键字值即在整体中的排序
    45             //cntB数组记录各第二关键字出现次数
    46         }
    47         for(int i=1;i<=n;i++) cntB[i]+=cntB[i-1];
    48         //计算i位置处当前第二关键字及之前的个数
    49         for(int i=n;i;i--)
    50             tsa[cntB[B[i]]--]=i;
    51         //tsa数组作临时sa
    52         //记录按第二关键字排序的sa
    53         for(int i=1;i<=n;i++) cntA[i]+=cntA[i-1];
    54         //计算当前第一关键字及之前的个数
    55         for(int i=n;i;i--)
    56             sa[cntA[A[tsa[i]]]--] = tsa[i];
    57         //此时求得的sa是最终的sa数组,综合了第一、二关键字
    58         //tsa从大到小进行安排,即从第二关键字排序靠后的开始分配
    59         //找到第二关键字排序最靠后的位置即tsa[i]
    60         //然后找到其第一关键字的排序A[tsa[i]]
    61         //更新其sa,
    62         //由于其第二关键字排序最靠后,
    63         //所以即使当前第一关键字顺序处有多个,
    64         //也为其在sa数组中相同第一关键字所在范围内分配最后一个位置,即,使其排序靠后
    65         rank[sa[1]] = 1;
    66         //原理同上,计算rank数组
    67         for(int i=2;i<=n;i++)
    68         {
    69             rank[sa[i]] = rank[sa[i-1]];
    70             if(A[sa[i]]!=A[sa[i-1]]||B[sa[i]]!=B[sa[i-1]])
    71                 rank[sa[i]]++;
    72             //因为当前是双关键字
    73         }
    74     }
    75     //此时求出了rank和sa数组
    76     //下面开始求height数组
    77     //感觉更像是求H数组
    78     //H[i] = height[rank[i]]
    79     for(int i=1;j=0;i<=n;i++)
    80     {
    81         //i表示在字符串中的位置,从1到n
    82         if(j) j--;
    83         //j为H[i-1]的值,即上次计算结果
    84         //利用了H[i]>=H[i-1]-1的性质
    85         //所以可以在此基础上计算
    86         while(ch[i+j] == ch[sa[rank[i]-1]+j]) j++;
    87         //sa[rank[i]-1]表示sa数组中前一个的后缀序号
    88         //累加计算最大公共前缀
    89         height[rank[i]] = j;
    90     }
    91 }
    View Code
  • 相关阅读:
    Nginx学习---Nginx的详解_【all】
    Nginx学习---企业级nginx环境搭建
    企业的批量分发的管理方案
    全网数据实时备份方案[inotify,sersync]
    搭建企业级全网数据定时备份方案[cron + rsync]2
    搭建企业级全网数据定时备份方案[cron + rsync]
    SSH连接不上
    ACC起来后,usb检测不到
    常用命令和配置文件位置
    Linux系统的信号详解
  • 原文地址:https://www.cnblogs.com/jsir2016bky/p/6003413.html
Copyright © 2011-2022 走看看