【学习笔记】后缀数组 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; 
posted @ 2025-05-03 16:23  Luzexxi  阅读(19)  评论(1)    收藏  举报