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);
}
}