题意 : 对于给出的串,输出其不同长度的子串的种类数
分析 : 有一个事实就是每一个子串必定是某一个后缀的前缀,换句话说就是每一个后缀的的每一个前缀都代表着一个子串,那么如何在这么多子串or后缀的前缀中找出不同的并计数呢?思路就是所有的可能子串数 - 重复的子串数。首先我们容易得到一个长度为 len 的串的子串数为 len * ( len + 1) / 2。那如何知道重复的子串数呢?答案就是利用后缀数组去跑一遍 Height ,得到所有的最长公共前缀(LCP),这些最长公共前缀的值都存在了 Height 中,对于任意两个后缀的最长公共前缀长度实际就是重复出现的子串数,那么只要遍历一遍 Height 数组,用刚刚得出来的总子串数减去所有的 Height 值即可
#include<bits/stdc++.h> using namespace std; ; int sa[maxn],s[maxn],wa[maxn], wb[maxn], Ws[maxn], wv[maxn]; int Rank[maxn], height[maxn]; bool cmp(int r[], int a, int b, int l){ return r[a] == r[b] && r[a+l] == r[b+l]; } void da(int r[], int sa[], int n, int m) { int i, j, p, *x = wa, *y = wb; ; i < m; ++i) Ws[i] = ; ; i < n; ++i) Ws[x[i]=r[i]]++; ; i < m; ++i) Ws[i] += Ws[i-]; ; i >= ; --i) sa[--Ws[x[i]]] = i; , p = ; p < n; j *= , m = p) { , i = n - j; i < n; ++i) y[p++] = i; ; i < n; ++i) if (sa[i] >= j) y[p++] = sa[i] - j; ; i < n; ++i) wv[i] = x[y[i]]; ; i < m; ++i) Ws[i] = ; ; i < n; ++i) Ws[wv[i]]++; ; i < m; ++i) Ws[i] += Ws[i-]; ; i >= ; --i) sa[--Ws[wv[i]]] = y[i]; , x[sa[]] = , i = ; i < n; ++i) x[sa[i]] = cmp(y, sa[i-], sa[i], j) ? p- : p++; } } void calheight(int r[], int sa[], int n) { ; ; i <= n; ++i) Rank[sa[i]] = i; ; i < n; height[Rank[i++]] = k) , j = sa[Rank[i]-]; r[i+k] == r[j+k]; k++); } char SS[maxn]; int S[maxn]; int main(void) { int nCase; scanf("%d", &nCase); while(nCase--){ scanf("%s", SS); int len = strlen(SS); ; i<len; i++) S[i] = (int)SS[i]; S[len] = ; da(S, sa, len+, ); calheight(S, sa, len); long long tmp = len; )) / ; ; i<=len; i++) ans -= height[i];//or ans += len - i - height[Rank[i]]; printf("%lld\n", ans); } ; }