标签:子串 后缀 枚举 笔记 height 算法 sa rk
SA
SA实际上求出两个数组\(sa,rk\)。
\(sa_i\)表示将所有后缀排序后排名第\(i\)小的后缀的编号,\(rk_i\)表示后缀\(i\)的排名。
满足其性质\(sa_{rk_i} = rk_{sa_i} = i\)
这里仅给出一个\(O(nlog^2n)\)的做法。
其他做法参见\(oiwiki\)。
点击查看代码
bool cmp(int i,int j){
if(rk[i]!=rk[j]){
return rk[i]<rk[j];
}else{
int ri,rj;
ri=i+k<n?rk[i+k]:-1;
rj=j+k<n?rk[j+k]:-1;
return ri<rj;
}
}
void calc(){
for(int i=0;i<n;i++){
rk[i]=s[i];
sa[i]=i;
}
for(k=1;k<n;k*=2){
sort(sa,sa+n,cmp);
tmp[sa[0]]=0;
for(int i=0;i<n-1;i++){
tmp[sa[i+1]]=tmp[sa[i]]+cmp(sa[i],sa[i+1]);
}
for(int i=0;i<n;i++){
rk[i]=tmp[i];
}
}
}
很多情况下我们都要求出辅助数组\(height\)数组。
\(height_i = lcp(sa_i,sa_{i - 1})\)
其有引理\(height_{rk_{i - 1}} + 1 \leq height_{rk_{i}}\)
所以根据该引理,维护一个指针即可\(O(n)\)求出其。
点击查看代码
for (i = 1, k = 0; i <= n; ++i) {
if (rk[i] == 0) continue;
if (k) --k;
while (s[i + k] == s[sa[rk[i] - 1] + k]) ++k;
height[rk[i]] = k;
}
其应用大概有:
两个子串最长公共前缀:
\(lcp(sa_i,sa_j) = \min{heaight_{i + 1...j}}\)
不同子串数目:
\(\frac{n(n+1))}{2} - \sum_{i = 2}^n height_i\)
连续的相同子串([NOI2016] 优秀的拆分):
考虑枚举长度然后设置关键点。
若存在\(AA\),则\(AA\)一定跨越两个关键点,枚举两关键点,其一定有\([1,x][1,y]\)的后缀最长公共子串加\([x,n][y,n]\)的前缀最长公共子串大于枚举长度。
配合图食用。
搭配其他数据结构
标签:子串,后缀,枚举,笔记,height,算法,sa,rk 来源: https://www.cnblogs.com/dixiao/p/15936062.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。