简述
后缀数组是一种后缀数据结构,记录一个字符串后缀按字典序排序的结果$SA_i$以及其与前后串$LCP$(最长公共前缀)$Height_i$
求法
定义
- $SA_i$表示$\text{Suff}_i$的排名
- $rank_i$表示排名为$i$的后缀是$\text{Suff}_{rank_i}$
- $Height_i$表示$LCP\{\text{Suff}_{SA_i},\text{Suff}_{SA_{i-1}}\}$
思想(基排)
从低位向高位排序,但直接做是$O(n^2)$的
瓶颈在于比较每次都与长度相关,考虑利用$rank$本身的表示的大小意义
考虑倍增
假设长度为$2^0$到$2^{k-1}$的排名已经求出
对于$i$开始长度为$2^k$的串,我们只需要比较$i$开始长度为$2^{k-1}$的串(第一关键字),$i+2^{k-1}$开始长度为$2^{k-1}$的串(第二关键字)
举个栗子:
比较前后两端就等于先排绿再排红
步骤
- 1.对每一位($k=0$)排序
1 | for(i=1;i<=n;++i)++sum[x[i]=s[i]]; |
2 | for(i=1;i<=m;++i)sum[i]+=sum[i-1]; |
3 | for(i=n;i;--i)SA[sum[x[i]]--]=i; |
- 2.利用$2^{k-1}$$rank$双关键字排序
理解一下在$swap$之前$y_i$表示第二关键字排名为$i$的起始位置
$fisrt_i$表示第二关键字排名为$i$的第一关键字的排名
1 | for(j=p=1;p<n;m=p,j<<=1){ |
2 | for(i=n-j+1,p=0;i<=n;++i)y[++p]=i; |
3 | for(i=1;i<=n;++i)if(SA[i]-j>0)y[++p]=SA[i]-j; |
4 | for(i=1;i<=n;++i)first[i]=x[y[i]]; |
5 | memset(sum,0,(m+1)<<2); |
6 | for(i=1;i<=n;++i)++sum[first[i]]; |
7 | for(i=1;i<=m;++i)sum[i]+=sum[i-1]; |
8 | for(i=n;i;--i)SA[sum[first[i]]--]=y[i]; |
9 | std::swap(x,y);x[SA[1]]=1; |
10 | for(i=2,p=1;i<=n;++i){ |
11 | if((y[SA[i-1]]!=y[SA[i]])||(y[SA[i-1]+j]!=y[SA[i]+j]))++p; |
12 | x[SA[i]]=p; |
13 | } |
14 | } |
- 3.利用性质$Height_{rank_i}\ge Height_{rank_{i-1}}-1$
1 | for(i=1;i<=n;++i){ |
2 | if(rank[i]>>1){j=SA[rank[i]-1];while(s[i+res]==s[j+res])++res;height[rank[i]]=res;if(res>0)--res;} |
3 | else height[rank[i]]=0; |
4 | } |
经典运用
后缀$LCP$
$LCP\{Suff_i , Suff_j\}$=$\displaystyle{\min_{k=rank_i+1}^{rank_j}Suff_k}$
【证明】我们可以知道$LCP$尽量长的后缀排名会越接近,因此求两两后缀的$LCP$一定是他们临近排名间被传递公用的部分
最长回文
首先原串变为$S+O+S^{-1}$
对奇数串
设中间点为$i$
若红与绿等长$l$则因为对称性回文长为$2l+1$
即$ans=2LCP(i,n-i)+1$
类似的偶数串
设中点后点为$i$
$ans=2LCP(i,n-i+1)$
最长公共子串
利用子串就是后缀的前缀,把他们拼起来
最长的前缀一定出现在连续(排名)两个后缀之间
判定一下位置即可
- 最长公共子串(n串)$BSOJ2815$
把$n$个串拼起来,二分长度
按排名看是否存在连续$n$个后缀(注意$belong$)前缀那么长
1 | inline char Check(re int mid){ |
2 | ++sign;re int i,res=0; |
3 | for(i=1;i<=len;++i){ |
4 | if(height[i]<mid)++sign,res=0; |
5 | if(col[belong[SA[i]]]^sign)col[belong[SA[i]]]=sign,++res; |
6 | if(res==n)return 1; |
7 | } |
8 | return 0; |
9 | } |
不同子串个数
结论$\displaystyle{ans=\sum_{i=1}^n}n-SA_i+1-height$
$Suff_{SA_i}$会形成$n-SA_i+1$个前缀,其中$Height_i$个与前面相同
题目 | 做法 | 简述 |
---|---|---|
$1355$ | 后缀数组+RMQ | |
$1590$ | 后缀数组 | |
$1856$ | 虚树+后缀自动机or后缀数组+单调栈 | |
$2144$ | 后缀数组+差分+二分 | |
$2230$ | 后缀数组 | |
$2801$ | 后缀数组+子串计数+容斥 | |
$2815$ | 后缀数组+二分+最长公共子串 | |
$2954$ | KMPor后缀数组 | |
$3166$ | 后缀数组 | |
$3167$ | 后缀数组 | |
$3236$ | 后缀数组+待修改 or 后缀自动机 | |
$3296$ | 后缀数组 | |
$3403$ | 后缀数组+最小表示法 | |
$3563$ | 后缀数组+Height+二分 | |
$3579$ | 后缀数组+Manachar | |
$3580$ | 后缀数组 | |
$3582$ | 后缀数组 | |
$3584$ | 后缀数组 | |
$3744$ | 后缀数组 | |
$4037$ | 后缀自动机+后缀数组 | |
$5209$ | 后缀数组 |