Loading

SPOJ-SUBST1 New Distinct Substrings 后缀数组

SPOJ-SUBST1 New Distinct Substrings 后缀数组

题意

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

\[T \leq 20 \\ |s| \leq 50000 \]

分析

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

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

\[ans = n\times (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';
		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);
	}
}
posted @ 2020-11-30 18:22  MQFLLY  阅读(107)  评论(0编辑  收藏  举报