[算法学习] 后缀排序

后缀排序

\(sa_i\) 表示后缀排名为\(i\)的位置。
\(rk_i\) 表示以\(i\)为首的后缀在所有后缀的排名。
\(LCP(i,j)\)表示\(sa_i\)\(sa_j\)的最长前缀的长度
\(height_i = LCP(i-1, i)\)
\(LCP(i,i) = len(sa[i]) = n - sa[i] + 1\)
\(LCP(i,j) = min(LCP(k - 1, k)) k \in [i + 1, j]\)

排序思想

利用倍增的思想。
定义\(s[i][k]\)表示以\(i\)开头的长度为\(2^k\)的字符串 (如果后缀长度\(<2^k\) 就以空格补齐)

那么如果要比较\(s[i][k+1]\)\(s[j][k+1]\)
那么就是要比较\(s[i][k] s[j][k]\) 如果相等
那么就比较\(s[i+2^k][k] s[j+2^k][k]\)

\(\Theta(n \log^2 n)\) 做法

inline bool cmp(int i, int j) {
	if(rank[i] != rank[j]) return rank[i] < rank[j];
	else {
		int ri = i + k < n ? rank[i + k] : -1;
		int rj = j + k < n ? rank[j + k] : -1;
		return ri < rj;
	}
}

int main() {
	scanf("%s", ch);
	n = strlen(ch); 
	for(int i = 0; i < n; i++) {
		sa[i] = i;
		rank[i] = ch[i];
	}
	for(k = 1; k < n; k <<= 1) {
		sort(sa, sa + n, cmp);
		tmp[sa[0]] = 0;
		for(int i = 1; i < n; i++) tmp[sa[i]] = tmp[sa[i - 1]] + (cmp(sa[i - 1], sa[i]) ? 1 : 0);
		for(int i = 0; i < n; i++) rank[i] = tmp[i];
	}
	for(int i = 0; i < n; i++) printf("%d ", sa[i] + 1);
	return 0;
}

利用基数排序可以直接优化到\(\Theta(n \log n)\)

struct SA {
	
	int n, m, x[N], y[N], sa[N], rk[N], buk[N], height[N];
	char s[N];	
	
	inline void GET_SA() {
	    for(int i = 1; i <= n; i++) x[i] = s[i], ++ buk[s[i]];
	    for(int i = 2; i <= m; i++) buk[i] += buk[i - 1];
	    for(int i = n; i >= 1; i--) sa[buk[x[i]] -- ] = i;
	    for(int k = 1; k <= n; k <<= 1) {
	        int num = 0;
	        for(int  i = n - k + 1; i <= n; i++) y[++num] = i;
	        for(int i = 1; i <= n; i++) if(sa[i] > k) y[++num] = sa[i] - k;
	        for(int i = 0; i <= m; i++) buk[i] = 0;
	        for(int i = 1; i <= n; i++) ++ buk[x[i]];
	        for(int i = 2; i <= m; i++) buk[i] += buk[i - 1];
	        for(int i = n; i >= 1; i--) sa[buk[x[y[i]]]--] = y[i], y[i] = 0;
	        swap(x, y);
	        x[sa[1]] = 1; num = 1;
	        for(int i = 2; i <= n; i++) x[sa[i]] = (y[sa[i]] == y[sa[i - 1]] && y[sa[i] + k] == y[sa[i - 1] + k]) ? num : ++num;
	        if(num == n) break;
	        m = num;
	    }
	    for(int i = 1; i <= n; i++) printf("%d ", sa[i]); puts("");
	}

	inline void GET_HEIGHT() {
		int k = 0;
		for(int i = 1; i <= n; i++) rk[sa[i]] = i;
		for(int i = 1; i <= n; i++) {
			if(rk[i] == 1) continue;
			if(k) k -- ;
			int j = sa[rk[i] - 1];
			while(j + k <= n && i + k <= n && s[i + k] == s[j + k]) ++ k;
			height[rk[i]] = k; 
		} 
		for(int i = 1; i <= n; i++) printf("%d ", height[i]); puts("");
	} 
} sa;
posted @ 2020-08-09 20:42  Hock  阅读(299)  评论(0编辑  收藏  举报