首页 > 代码库 > hihocoder1445 后缀自动机二·重复旋律5

hihocoder1445 后缀自动机二·重复旋律5

传送门:http://hihocoder.com/problemset/problem/1445

【题解】

大概看了一天的后缀自动机,总算懂了一些

这篇文章写的非常好,诚意安利:Suffix Automaton Tutorial - Hunt Zhan

我就是看了这个大概懂了。

整个过程大概是:每次插入一个state可能会分裂原有的state的transition,就维护这个transition即可。

要用par[x]记录suffix-link是谁。

过程中可以不用记录minlen,因为$minlen[i] = maxlen[par[i]] + 1$,这点非常重要。

这题前面写了后缀自动机,然后上面像统计子树一样统计,后来发现,复杂度是$O(n * 26)$,似乎太大了,TLE了。

其实后缀自动机的每个state都含有若干子串,所有state的含有子串个数之和就是总个数了。

每个state含有子串个数为$maxlen[i] - minlen[i]$,这里把$minlen[i]$替换成$maxlen[par[i]] + 1$即可,不需要再求$minlen[i]$。

这样复杂度就差不多$O(n)$了,常数小了。

技术分享
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm>
// # include <bits/stdc++.h>

using namespace std;

typedef long long ll;
typedef long double ld;
typedef unsigned long long ull;
const int N = 1e6 + 10, M = 2e6 + 3;
const int mod = 1e9+7;

char str[N];
struct SAM {
    int ch[M][26], par[M], ml[M];
    int siz, rt, last;
    inline void set() {
        siz = rt = last = 1;
        memset(ch, 0, sizeof ch);
        memset(par, 0, sizeof par);
        memset(ml, 0, sizeof ml);
    }
    inline void extend(int c) {
        int p = last, cur = ++siz;
        ml[cur] = ml[p] + 1;
        for (; p && !ch[p][c]; p = par[p]) ch[p][c] = cur;
        if(!p) par[cur] = rt;
        else {
            int q = ch[p][c];
            if(ml[q] == ml[p] + 1) par[cur] = q;
            else {
                int sq = ++siz;
                par[sq] = par[q];
                for (int i=0; i<26; ++i) ch[sq][i] = ch[q][i];
                ml[sq] = ml[p] + 1;
                par[q] = par[cur] = sq;
                for (; p && ch[p][c] == q; p = par[p]) ch[p][c] = sq;                    
            }
        }
        last = cur;
    }    
}S;

/*
ll f[M];
inline void dfs(int x) {
    f[x] = 1;
    for (int i=0; i<26; ++i) {
        if(!S.ch[x][i]) continue;
        dfs(S.ch[x][i]);
        f[x] += f[S.ch[x][i]];
    }
}
*/

int main() {
    scanf("%s", str+1); S.set();
    for (int i=1; str[i]; ++i) S.extend(str[i] - a);    
    ll ans = 0;
    for (int i=1; i<=S.siz; ++i) {
        if(i == S.rt) continue;
        ans += S.ml[i] - S.ml[S.par[i]];
    }
    cout << ans;
    return 0;
}
View Code

 

hihocoder1445 后缀自动机二·重复旋律5