[BZOJ 3238] [AHOI 2013] 差异 【后缀数组 + 单调栈】

题目链接:BZOJ - 3238

 

题目分析

显然,这道题就是求任意两个后缀之间的LCP的和,这与后缀数组的联系十分明显。

求出后缀数组后,求出字典序相邻两个后缀的LCP,即 Height 数组。

那么我们可以用这个 Height 数组求出所有后缀之间 LCP 的和。

我们用 f[i] 表示字典序第 i 的后缀与字典序在 i 之后的所有后缀的 LCP 的和。

我们知道,两个后缀的 LCP 为 Height 数组中这两个后缀之间的最小值。

我们从最后向前推 i ,用一个单调栈维护后面的 Height 单调不上升,然后用 St[Top] 来推 f[i] 即可,具体见代码。

 

代码

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
 
using namespace std;
 
const int MaxL = 500000 + 5;
 
typedef long long LL;
 
LL Ans, Temp;
LL f[MaxL];
 
int n, Top;
int A[MaxL], Rank[MaxL], SA[MaxL], Height[MaxL], St[MaxL];
int VA[MaxL], VB[MaxL], VC[MaxL], Sum[MaxL];
 
char S[MaxL];
 
inline bool Cmp(int *a, int x, int y, int l) {
    return (a[x] == a[y]) && (a[x + l] == a[y + l]);
}
 
void DA(int *A, int n, int m) {
    int *x, *y, *t;
    x = VA; y = VB;
    for (int i = 1; i <= m; ++i) Sum[i] = 0;
    for (int i = 1; i <= n; ++i) ++Sum[x[i] = A[i]];
    for (int i = 2; i <= m; ++i) Sum[i] += Sum[i - 1];
    for (int i = n; i >= 1; --i) SA[Sum[x[i]]--] = i;
    int p, q;
    p = 0;
    for (int j = 1; p < n; j <<= 1, m = p) {   
        q = 0;
        for (int i = n - j + 1; i <= n; ++i) y[++q] = i;
        for (int i = 1; i <= n; ++i) {
            if (SA[i] <= j) continue;
            y[++q] = SA[i] - j;
        }
        for (int i = 1; i <= m; ++i) Sum[i] = 0;
        for (int i = 1; i <= n; ++i) VC[i] = x[y[i]];
        for (int i = 1; i <= n; ++i) ++Sum[VC[i]];
        for (int i = 2; i <= m; ++i) Sum[i] += Sum[i - 1];
        for (int i = n; i >= 1; --i) SA[Sum[VC[i]]--] = y[i];
        t = x; x = y; y = t; p = 1;
        x[SA[1]] = 1;
        for (int i = 2; i <= n; ++i) 
            x[SA[i]] = Cmp(y, SA[i], SA[i - 1], j) ? p : ++p;
    }
    for (int i = 1; i <= n; ++i) Rank[SA[i]] = i;
     
    //GetHeight
    int h = 0, o;
    for (int i = 1; i <= n; ++i) {
        if (Rank[i] == 1) continue;
        o = SA[Rank[i] - 1];
        while (A[i + h] == A[o + h]) ++h;
        Height[Rank[i]] = h;
        if (h > 0) --h;
    }
}
 
int main() 
{
    scanf("%s", S + 1);
    n = strlen(S + 1);
    for (int i = 1; i <= n; ++i) A[i] = S[i] - 'a' + 1;
    DA(A, n, 26);
    Ans = 0ll; Temp = 0ll;
    for (int i = 1; i <= n; ++i) 
        Ans += (LL)(n - i + 1) * (LL)(n - 1);
    Top = 0;
    St[++Top] = n + 1;
    for (int i = n; i >= 2; --i) {
        while (Top > 0 && Height[St[Top]] > Height[i]) --Top;
        int x = St[Top];
        f[i] = (LL)Height[i] + (LL)Height[i] * (x - i - 1) + (LL)f[x];
        Temp += f[i];
        St[++Top] = i;
    }
    Ans -= Temp * 2ll;
    printf("%lld\n", Ans);
    return 0;
}

  

posted @ 2015-01-10 10:54  JoeFan  阅读(184)  评论(0编辑  收藏  举报