题意:给定一个字符串,求至少重复一次的不同子串个数。
分析:模拟写出子符串后缀并排好序可以发现,每次出现新的重复子串个数都是由现在的height值减去前一个height值。
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #include <queue> using namespace std; const int maxn = 100010; typedef long long LL; int sa[maxn]; int t1[maxn], t2[maxn], c[maxn]; int ran[maxn], height[maxn]; int s[maxn]; char str[maxn]; void build_sa(int s[], int n, int m) { int i, j, p, *x = t1, *y = t2; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[x[i] = s[i]]++; for (i = 1; i < m; i++) c[i] += c[i-1]; for (i = n-1; i >= 0; i--) sa[--c[x[i]]] = i; for (j = 1; j <= n; j <<= 1) { p = 0; for (i = n-j; i < n; i++) y[p++] = i; for (i = 0; i < n; i++) if (sa[i] >= j) y[p++] = sa[i] - j; for (i = 0; i < m; i++) c[i] = 0; for (i = 0; i < n; i++) c[x[y[i]]]++; for (i = 1; i < m; i++) c[i] += c[i-1]; for (i = n-1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i]; swap(x, y); p = 1, x[sa[0]] = 0; for (i = 1; i < n; i++) x[sa[i]] = y[sa[i-1]] == y[sa[i]] && y[sa[i-1]+j] == y[sa[i]+j] ? p-1 : p++; if (p >= n) break; m = p; } } void getHeight(int s[],int n) { int i, j, k = 0; for (i = 0; i <= n; i++) ran[sa[i]] = i; for (i = 0; i < n; i++) { if (k) k--; j = sa[ran[i]-1]; while (s[i+k] == s[j+k]) k++; height[ran[i]]=k; } } int main() { int T; scanf("%d", &T); while (T--) { scanf("%s", str); int n = strlen(str); for (int i = 0; i <= n; i++) s[i] = str[i]; build_sa(s, n+1, 128); getHeight(s, n); LL ans = 0; for (int i = 1; i <= n; i++) { if(height[i]>height[i-1])ans+=height[i]-height[i-1]; } printf("%lld\n", ans); } return 0; }