sa后缀数组7.28
SA后缀数组
【模板】后缀数组
理解全在代码里
定义 :
\(sa[i]\) 表示排名为i的后缀的开头位置
\(rk[i]\) 表示以i为开头的后缀的排名(这里\(i\)均表示下标)
\(tmp[i]\) 表示i对应的i-w的位置,为后面倍增两两合并准备
\(height[i]\) 表示\(lcp{({sa[i]},{sa[i - 1]})}\)
\(cnt[i]\) 计数排序的计数器
void get_sa(){
int m = 255 ;
for(int i = 1 ; i <= n ;++i)cnt[rnk[i] = s[i]]++; //初始化,给长度为1的赋值
for(int i = 1 ; i <= m ;++i)cnt[i] += cnt[i - 1] ;//前缀和,计算
for(int i = n ; i >= 1 ;--i)sa[cnt[rnk[i]]--] = i ;
for(int w = 1 , t = 0 ; ; m = t , t = 0 , w <<= 1 ){//每次倍增长度
for(int i = n - w + 1 ; i <= n ;++i)tmp[++t] = i;//如果 i + w > 0 就把他平行移到左边
for(int i = 1 ; i <= n ;++i)
if( sa[i] > w )tmp[++t] = sa[i] - w ;//否则直接赋值
// for(int i = 1 ; i <= n ; i++){
// cout << i << " " << tmp[i] << " " << w << endl ;
// }
// cout << endl ;
for(int i = 0 ; i <= m ;++i)cnt[i] = 0 ;//重置计数器
for(int i = 1 ; i <= n ;++i)cnt[rnk[tmp[i]]]++;//对第一关键字进行计数排序
for(int i = 1 ; i <= m ;++i)cnt[i] += cnt[i - 1];//前缀和
for(int i = n ; i >= 1 ;--i)sa[cnt[rnk[tmp[i]]]--]= tmp[i];//计算sa
swap(rnk , tmp) , t = 0 ;//这里应该是节省空间 直接使用把老的rnk赋给tmp , 用老的rnk更新新的rnk
for(int i = 1 ; i <= n ; i++)
rnk[sa[i]] = (tmp[sa[i]] == tmp[sa[i - 1]] and tmp[sa[i] + w] == tmp[sa[i - 1] + w] ) ? t : ++t ;
if( t == n )break ;//如果已经够了就不再排序了
}
}
void getHeight(){
for(int i = 1 , k = 0 ; i <= n ; ++i){
if(k)--k;
while(s[i + k] == s[sa[rnk[i] - 1] + k])k++;//直接暴力扩展
height[rnk[i]] = k ;
}
}
getHeight函数复杂度均摊\(O(n)\)
get_sa函数复杂度\(O(n{logn})\)
应用:
1.比较两个子串的大小关系
2.不同子串的数目
不同子串个数有 :\(\\{n*(n+1)/2 + sum_{i=1}height[i]}\) 种

浙公网安备 33010602011771号