SPOJ-SUBST1 New Distinct Substrings 后缀数组

SPOJ-SUBST1 New Distinct Substrings 后缀数组

题意

给定一个字符串,求其中本质不同的子串

[T leq 20 \ |s| leq 50000 ]

分析

直接找不同的有点麻烦,不妨反着考虑本质相同的子串的个数

height数组的含义是(sa[i])(sa[i - 1])的后缀的公共前缀的长度。这个公共前缀是本质相同的,所以需要减去

[ans = n imes (n + 1) / 2 - sum height[i] ]

代码

char ss[maxn];
int s[maxn];
int sa[maxn],t[maxn],t2[maxn],c[maxn],n;
int len;

void build_sa(int m){
	int *x = t,*y = t2;
	for(int i = 0;i < m;i++) c[i] = 0;
	for(int i = 0;i < n;i++) c[x[i] = s[i]]++;
	for(int i = 1;i < m;i++) c[i] += c[i -1];
	for(int i = n - 1;i >= 0;i--) sa[--c[x[i]]] = i;
	for(int k = 1;k <= n;k <<= 1){
		int p = 0;
		for(int i = n - k;i < n;i++) y[p++] = i;
		for(int i = 0;i < n;i++) if(sa[i] >= k) y[p++] = sa[i] - k;
		for(int i = 0;i < m;i++) c[i] = 0;
		for(int i = 0;i < n;i++) c[x[y[i]]]++;
		for(int i = 0;i < m;i++) c[i] += c[i - 1];
		for(int i = n - 1;i >= 0;i--) sa[--c[x[y[i]]]] = y[i];
		swap(x,y);
		p = 1;
		x[sa[0]] = 0;
		for(int i = 1;i < n;i++)
			x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i] + k] == y[sa[i - 1] + k] ? p - 1:p++;
		if(p >= n) break;
		m = p;
	}
}

int rak[maxn],height[maxn];

void getHeight(){
	int k = 0;
	for(int i = 0;i < n;i++) rak[sa[i]] = i;
	for(int i = 0;i < n;i++){
		if(k) k--;
		int j = sa[rak[i] - 1];
		while(s[i + k] == s[j + k]) k++;
		height[rak[i]] = k;
	}
}

int main(){
	int T = readint();
	while(T--){
		scanf("%s",ss);
		n = strlen(ss);
		for(int i = 0;i < n;i++){
			s[i] = ss[i];
		}
		s[n++] = 0;
		build_sa(200);
		getHeight();
		ll res = (ll)n * (n - 1) / 2;
		for(int i = 0;i <= n ;i++){
			res -= height[i];
		}
		cout << res << '
';
		n++;
		memset(sa,0,sizeof(int) * n);
		memset(rak,0,sizeof(int) * n);
		memset(t,0,sizeof(int) * n);
		memset(t2,0,sizeof(int) * n);
		memset(height,0,sizeof(int) * n);
		memset(s,0,sizeof(int) * n);
	}
}
原文地址:https://www.cnblogs.com/hznumqf/p/14062797.html