后缀数组-倍增法

Posted 西小贝

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了后缀数组-倍增法相关的知识,希望对你有一定的参考价值。

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

链接: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

 

以上是关于后缀数组-倍增法的主要内容,如果未能解决你的问题,请参考以下文章

用倍增法求后缀数组名次数组-JavaScript

后缀数组-倍增法

后缀数组倍增法理解

后缀数组

后缀数组(模板)

后缀数组之倍增算法