[后缀自动机][阈值] LOJ #6031. 「雅礼集训 2017 Day1」字符串

Solution S o l u t i o n

因为 qk=P q k = P 是一个常数,想到可以阈值。
k<S k < S 时,对于每个串可以 O(k2) O ( k 2 ) 暴力在后缀自动机上跑出现次数。对于一个区间 [l,r] [ l , r ] ,直接计算在这个串上的贡献就好了。时间复杂度应该是 O(qk2logm)=O(Pklogm) O ( q k 2 log ⁡ m ) = O ( P k log ⁡ m ) 的???
k>S k > S 时,可以对于这个串的每个前缀都记录下沿着后缀自动机走到的节点和匹配的长度,这样的话只要这个后缀的前缀长度合法就好了,这可以在 parent p a r e n t 树上倍增。时间复杂度可能是 O(qmlogk)=O(Pkmlogn) O ( q m log ⁡ k ) = O ( P k m log ⁡ n ) 。。
复杂度一通乱分析
反正的话可能得找一下 S S <script type="math/tex" id="MathJax-Element-20">S</script>的值。。。刚开始就T了。。

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;
typedef long long ll;
typedef pair<int, int> Pairs;

const int N = 202020;
const int M = 722;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}
inline void read(char *s) {
    static char c; int len = 0;
    for (c = get(); c < 'a' || c > 'z'; c = get());
    for (; c >= 'a' && c <= 'z'; c = get()) s[len++] = c;
    s[len] = 0;
}

namespace SAM {
    int go[N][26];
    int par[N], rgt[N], mx[N];
    int fa[N][26];
    int buc[N], id[N];
    int root, last, tcnt;
    inline void init(void) {
        root = last = tcnt = 1;
    }
    inline int extend(int c) {
        int p = last, np = ++tcnt;
        mx[np] = mx[p] + 1; rgt[np] = 1;
        for (; p && !go[p][c]; p = par[p]) go[p][c] = np;
        if (p) {
            int q = go[p][c];
            if (mx[q] == mx[p] + 1) {
                par[np] = q;
            } else {
                int nq = ++tcnt;
                mx[nq] = mx[p] + 1;
                par[nq] = par[q];
                par[q] = par[np] = nq;
                memcpy(go[nq], go[q], sizeof go[q]);
                for (; go[p][c] == q; p = par[p]) go[p][c] = nq;
            }
        } else {
            par[np] = root;
        }
        return last = np;
    }
    inline void pre(int fl = 0) {
        for (int i = 1; i <= tcnt; i++) ++buc[mx[i]];
        for (int i = 1; i <= tcnt; i++) buc[i] += buc[i - 1];
        for (int i = tcnt; i; i--) id[buc[mx[i]]--] = i;
        for (int i = tcnt; i; i--) rgt[par[id[i]]] += rgt[id[i]];
        if (fl) {
            for (int i = 1; i <= tcnt; i++) *fa[i] = par[i];
            for (int i = 1; i <= 20; i++)
                for (int u = 1; u <= tcnt; u++)
                    fa[u][i] = fa[fa[u][i - 1]][i - 1];
        }
    }
    inline int count0(int &u, int c) {
        u = go[u][c]; return rgt[u];
    }
    inline void match(int &u, int &len, int c) {
        while (u && !go[u][c]) {
            u = par[u]; len = mx[u];
        }
        if (u) {
            u = go[u][c]; ++len;
        } else {
            u = root; len = 0;
        }
    }
    inline int count1(int u, int k) {
        for (int i = 20; ~i; i--)
            if (mx[fa[u][i]] >= k) u = fa[u][i];
        return rgt[u];
    }
}

int n, m, q, k, S;
char s[N], w[N];
int l[N], r[N], id[N];

namespace work0 {
    int ans[M][M];
    int x, y;
    vector<int> seg[M][M];
    inline void solve(void) {
        for (int i = 0; i < m; i++) {
            read(x); read(y);
            seg[x][y].push_back(i);
        }
        for (int i = 0; i < n; i++) SAM::extend(s[i] - 'a');
        SAM::pre();
        for (int i = 1; i <= q; i++) {
            read(w); read(x); read(y);
            ll ans = 0;
            for (int i = 0; i < k; i++) {
                int u = SAM::root;
                for (int j = i; j < k; j++) {
                    int cnt = SAM::count0(u, w[j] - 'a');
                    if (u == 0) break;
                    auto l = lower_bound(seg[i][j].begin(), seg[i][j].end(), x);
                    auto r = upper_bound(seg[i][j].begin(), seg[i][j].end(), y);
                    ans += (ll)(r - l) * cnt;
                }
            }
            printf("%lld\n", ans);
        }
    }
}
namespace work1 {
    int x, y;
    int pos[N], len[N];
    int l[N], r[N];
    inline void solve(void) {
        for (int i = 0; i < m; i++) {
            read(l[i]); read(r[i]);
        }
        for (int i = 0; i < n; i++) SAM::extend(s[i] - 'a');
        SAM::pre(1);
        for (int i = 1; i <= q; i++) {
            read(w); read(x); read(y);
            int u = 1, lent = 0, ans = 0;
            for (int j = 0; j < k; j++) {
                SAM::match(u, lent, w[j] - 'a');
                pos[j] = u; len[j] = lent;
            }
            for (int j = x; j <= y; j++) {
                int lent = r[j] - l[j] + 1;
                if (lent > len[r[j]]) continue;
                ans += SAM::count1(pos[r[j]], lent);
            }
            printf("%lld\n", ans);
        }
    }
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    SAM::init();
    read(n); read(m); read(q); read(k);
    read(s);
    S = 555;
    if (k < S) work0::solve();
    else work1::solve();
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值