题目地址
题目地址
题解
很棒的一道后缀数组题!
其实就是对原串求出每一个前缀的本质不同子串的个数。
求一个串的本质不同子串数是一个经典问题,其为\(\frac {n \times (n + 1)} 2 - \sum \text{height[i]}\)。
考虑插入前缀的过程,这种情况下的\(\text {sa}\)数组和\(\text {height}\)数组是在不断改变的,这样就很不好统计了。
那么不妨把原串翻转过来,这样加入一个字符其实就是加入一个新的后缀而已。
而我们知道\(\text{LCP(x,y)} = \min_{k=x+1}^y \{\text{height[k]}\}\),所以在后缀没有加全的情况下其实也是可以知道两个串的\(\text {LCP}\)的,只需要预处理一个\(\text{ST}\)表即可。
考虑一下加入一个后缀对当前情况下\(\text {height}\)数组的影响,在进行一次后缀排序后,假设其位置为\(pos\),那么只会影响到\(\text {height[pos]}\) 和 \(\text {height[pos + 1]}\)。
所以只需要动态修改这两个东西即可。
这两个东西可以用平衡树来维护。具体的,在每次插入一个数后,找出它的前驱和后继,给\(\sum \text{height[i]}\)加上\(\text{LCP}(pre, pos)\)和\(\text{LCP}(pos, next)\)。
平衡树可以上\(\text{set}\)。
#include
using namespace std;
typedef long long ll;
const int N = 100010;
ll ans = 0;
int n, m, a[N], b[N], f[N][18], LG[N];
int sa[N], height[N], rnk[N], tp[N], tong[N];
set s;
void radix_sort() {
for(int i = 1; i <= m; ++i) tong[i] = 0;
for(int i = 1; i <= n; ++i) tong[rnk[i]]++;
for(int i = 1; i <= m; ++i) tong[i] += tong[i - 1];
for(int i = n; i; --i) sa[tong[rnk[tp[i]]]--] = tp[i];
}
void suffix_sort() {
for(int i = 1; i <= n; ++i) rnk[i] = a[i], tp[i] = i;
m = n; radix_sort();
for(int w = 1, p = 1; p < n && w <= n; w <<= 1, m = p) {
p = 0;
for(int i = 1; i <= w; ++i) tp[++p] = n - w + i;
for(int i = 1; i <= n; ++i) if(sa[i] > w) tp[++p] = sa[i] - w;
radix_sort(); swap(tp, rnk); rnk[sa[1]] = p = 1;
for(int i = 2; i <= n; ++i)
rnk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] && tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
}
for(int i = 1, k = 0; i <= n; ++i) {
if(k) --k;
int j = sa[rnk[i] - 1];
while(a[i + k] == a[j + k] && i + k <= n && j + k <= n) ++k;
height[rnk[i]] = k;
}
}
int query(int l, int r) {
int k = LG[r - l + 1];
return min(f[l][k], f[r - (1 << k) + 1][k]);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]), b[i] = a[i];
sort(b + 1, b + n + 1);
for(int i = 1; i <= n; ++i) a[i] = lower_bound(b + 1, b + n + 1, a[i]) - b;
reverse(a + 1, a + n + 1); suffix_sort();
LG[2] = 1;
for(int i = 3; i <= n; ++i) LG[i] = LG[i >> 1] + 1;
for(int j = 1; j <= n; ++j) f[j][0] = height[j];
for(int j = 1; j <= 19; ++j) {
for(int i = 1; i + (1 << j) - 1 <= n; ++i)
f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
}
ans = 0;
for(int i = n; i; --i) {
set::iterator it = s.insert(rnk[i]).first, p1 = it, p2 = it;
if(it != s.begin()) {
p1--;
ans += query(*p1 + 1, rnk[i]);
if(++p2 != s.end()) {
ans += query(rnk[i] + 1, *p2);
ans -= query(*p1 + 1, *p2);
}
} else if(++p2 != s.end())
ans += query(rnk[i] + 1, *p2);
printf("%lld\n", 1LL * (n - i + 1) * (n - i + 2) / 2LL - ans);
}
return 0;
}