后缀数组-倍增法
额,写的有点混乱,改天整理一下
链接: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 }