zoukankan      html  css  js  c++  java
  • [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
    ", Ans);
        return 0;
    }
    

      

  • 相关阅读:
    linux —— 学习笔记(汇总)
    linux —— ubuntu 初次安装问题
    更改CMD默认的初始路径
    深入浅出理解linux inode结构
    重拾简单的linux指令之info 【转】
    Python 中数据的序列化和反序列化(json处理)
    day07
    Python 的反射机制
    Python 的 __new__()方法与实例化
    Classes as objects
  • 原文地址:https://www.cnblogs.com/JoeFan/p/4214575.html
Copyright © 2011-2022 走看看