[BZOJ3238][Ahoi2013]差异

[Ahoi2013]差异

Description

Input
一行,一个字符串S
Output
一行,一个整数,表示所求值
Sample Input
cacao
Sample Output
54
HINT
2<=N<=500000,S由小写英文字母组成

Solution
利用SAM建出后缀树,在后缀树上dp。
对于后缀树上一个节点 i ,考虑它的任意两个儿子 son1,son2 ,如果 son1,son2 中有 sum[son1],sum[son2] 个表示后缀的节点,那么 i LCP sum[son1]sum[son2]

Code

#include 
using namespace std;
#define rep(i, l, r) for (int i = (l); i <= (r); i++)
#define per(i, r, l) for (int i = (r); i >= (l); i--)
#define REP(i, n) for (int i = 0; i < (n); i++)
#define PER(i, n) for (int i = (n)-1; i; i--)
#define MS(_) memset(_, 0, sizeof(_))
#define MP make_pair
#define PB push_back
typedef long long ll;
template<typename T> inline void read(T &x){
    x = 0; T f = 1; char ch = getchar();
    while (!isdigit(ch)) { if (ch == '-') f = -1; ch = getchar(); }
    while (isdigit(ch)) { x = x * 10 + ch - '0'; ch = getchar(); }
    x *= f;
}
template<typename T> inline void ckmax(T &a, T b){ if (a < b) a = b; }
template<typename T> inline void ckmin(T &a, T b){ if (a > b) a = b; }
template<typename T> inline T sqr(T x){ return x * x; }

const int MaxN = 1000010;
int nxt[MaxN][26], fa[MaxN], d[MaxN], q[MaxN], n;
ll sum[MaxN][2], len[MaxN];
char str[MaxN];
bool suf[MaxN];

struct SAM{
    int root, last, cnt;
    SAM(){ root = last = ++cnt; }
    inline void ins(int c){
        int np = ++cnt, p = last; last = np;
        len[np] = len[p] + 1; sum[np][0] = 1ll; suf[np] = true;
        for (; p && !nxt[p][c]; nxt[p][c] = np, p = fa[p]);
        if (!p) fa[np] = root;
        else if (len[nxt[p][c]] == len[p] + 1) fa[np] = nxt[p][c];
        else{
            int nq = ++cnt, q = nxt[p][c]; len[nq] = len[p] + 1;
            memcpy(nxt[nq], nxt[q], sizeof(nxt[q])); fa[nq] = fa[q];
            fa[np] = fa[q] = nq;
            for (; p && nxt[p][c] == q; nxt[p][c] = nq, p = fa[p]);
        }
    }
    inline void build(){
        scanf("%s", str+1); n = strlen(str+1);
        per(i, n, 1) ins(str[i]-'a');
    }
    inline void topsort(){
        rep(i, 1, cnt) d[len[i]]++;
        rep(i, 1, n) d[i] += d[i-1];
        per(i, cnt, 1) q[d[len[i]]--] = i;
    }
    inline void cal(){ 
        topsort();
        per(i, cnt, 1) sum[fa[q[i]]][0] += sum[q[i]][0]; 
        per(i, cnt, 1) sum[fa[q[i]]][1] += sqr(sum[q[i]][0]);
    }
    inline void solve(){ ll ans = 0, res = 0;
        rep(i, 1, n-1) ans += 1ll*(n-i+1)*(n-i) + 1ll*(1+n-i)*(n-i)/2;
        rep(i, 1, cnt){
            if (suf[i]) sum[i][0]--;
            res += 1ll * ((sqr(sum[i][0]) - sum[i][1]) >> 1) * len[i];
            if (suf[i]) res += 1ll * sum[i][0] * len[i], sum[i][0]++;
        }
        printf("%lld\n", ans-2*res);
    }
}sam;

int main(){
    sam.build();
    sam.cal();
    sam.solve();
    return 0;
}

你可能感兴趣的:(后缀自动机/后缀数组)