【学习笔记】后缀数组 SA
板子:
对后缀排序 \(n \le 10^6\)
\(O(n \log^2n)\):sort 的 cmp 中二分用 Hash 比较;
\(O(n \log^2n)\):倍增,每次对 \(s[i\sim i+2^{k-1}]\) 排序;
\(O(n \log n)\):每次排序用基数排序解决;
\(O(n \log n)\):优化 ①第二关键字直接放进来;②记录 rk 种类,优化值域;③所有 rk 都不同直接 break。
代码
struct SuffixAarry{
int cnt[N],id[N];
int rk[N],sa[N],oldrk[N];
int h[N];
char s[N];
void SA(){
int m = 256, p = 0;
memset(cnt,0,sizeof cnt);
rep(i, 1, n)cnt[rk[i] = s[i]]++;
rep(i, 1, m)cnt[i] += cnt[i-1];
per(i, n, 1)sa[cnt[rk[i]]--] = i;
for(int w = 1;;w <<= 1,m = p){
int it = 0;
rep(i, n-w+1, n)id[++it] = i;
rep(i, 1, n)if(sa[i] > w)id[++it] = sa[i] - w;
memset(cnt,0,sizeof cnt);
rep(i, 1, n)cnt[rk[i]]++;
rep(i, 1, m)cnt[i] += cnt[i-1];
per(i, n, 1)sa[cnt[rk[id[i]]]--] = id[i];
p = 0;
memcpy(oldrk, rk, sizeof rk);
rep(i, 1, n){
if(oldrk[sa[i]] == oldrk[sa[i-1]] && oldrk[sa[i]+w] == oldrk[sa[i-1]+w])rk[sa[i]] = p;
else rk[sa[i]] = ++p;
}
if(p == n)break;
}
}
int st[N][21],lg[N];
inline void geth(){
for(int i = 1,k = 0;i<=n;++i){
if(k)--k;
while(s[i+k] == s[sa[rk[i]-1]+k])++k;
h[rk[i]] = k;
}
rep(i, 2, n)lg[i] = lg[i>>1] + 1;
rep(i, 1, n)st[i][0] = h[i];
rep(j, 1, 20)for(int i = 1;i+(1<<(j-1))-1<=n;++i)
st[i][j] = min(st[i][j-1],st[i+(1<<(j-1))][j-1]);
}
inline int qry(int l,int r){
l = rk[l], r = rk[r];
if(l > r)swap(l, r); ++l;
int t = lg[r-l+1];
return min(st[l][t], st[r-(1<< t)+1][t]);
}
void init(){ SA(), geth(); }
}LCP;

浙公网安备 33010602011771号