[回文自动机][阈值] BZOJ 4932: 基因

Solution

  • 关于回文自动机的前端插入:一个回文串的回文前缀显然是其回文后缀。实现两端插入需要记录两个指针 lastback,lastfront ,注意当最长回文前(后)缀为原串时,两个指针就相同了。
  • 一个串的子串的回文自动机是这个串的回文自动机的子图。因为增量法构造回文自动机时是不会改变原有的自动机的。

这道题的话阈值 B=n ,每 B 个点建一个到最右端的回文自动机。
查询从每个关键点向右前端插入字符,并判断新增的回文串与右端点的关系。
这样的做法应该不能用基于势能分析的插入方法了吧。。。
感觉对quick数组可持久化常数会比较大???

#include <bits/stdc++.h>
using namespace std;

const int N = 101010;
const int M = 350;

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;
}

char s[N];
int last1, last2, Tcnt;
int par[N], len[N], ri[N];
int ch[N][30], qui[N][30];
int pf[M][N], ans[M][N], pt[M][N];
int pre[N];
int type, lans, clc;
int n, q, l, r, B;
int bel[N];

inline void ExtendFront(int pos, int r) {
    int p = last2, key = s[pos];
    if (s[pos + len[p] + 1] != key || pos + len[p] + 1 > r) p = qui[p][key];
    if (!ch[p][key]) {
        int np = ++Tcnt, q = par[p];
        len[np] = len[p] + 2;
        if (s[pos + len[q] + 1] == key) par[np] = ch[q][key];
        else ch[qui[q][key]][key];
        memcpy(qui[np], qui[par[np]], sizeof qui[np]);
        qui[np][s[pos + len[par[np]]]] = par[np];
        ch[p][key] = np;
    }
    last2 = ch[p][key];
    if (len[last2] == r - pos + 1) last1 = last2;
}
inline void ExtendBack(int l, int pos) {
    int p = last1, key = s[pos];
    if (s[pos - len[p] - 1] != key || pos - len[p] - 1 < l) p = qui[p][key];
    if (!ch[p][key]) {
        int np = ++Tcnt, q = par[p];
        len[np] = len[p] + 2;
        if (s[pos - len[q] - 1] == key) par[np] = ch[q][key];
        else par[np] = ch[qui[q][key]][key];
        memcpy(qui[np], qui[par[np]], sizeof qui[np]);
        qui[np][s[pos - len[par[np]]]] = par[np];
        ch[p][key] = np;
    }
    last1 = ch[p][key];
    if (len[last1] == pos - l + 1) last2 = last1;
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(type); read(n); read(q);
    read(s + 1); s[0] = -1;
    for (int i = 1; i <= n; i++) s[i] -= 'a';
    B = sqrt(n);
    for (int i = 1; i <= n; i++) bel[i] = (i - 1) / B + 1;

    memset(pt, 0x3f, sizeof pt);
    par[0] = par[1] = 1; len[1] = -1; Tcnt = 1;
    for (int i = 0; i < 26; i++) qui[0][i] = qui[1][i] = 1;

    for (int i = 0; i < bel[n]; i++) {
        ++clc; last1 = last2 = 0;
        int st = i * B + 1;
        for (int j = st; j <= n; j++) {
            ExtendBack(st, j);
            pf[i][j] = last2; ans[i][j] = ans[i][j - 1];
            if (pre[last1] != clc) {
                ++ans[i][j]; pre[last1] = clc;
                pt[i][last1] = j;
            }
        }
    }

    while (q--) {
        read(l); read(r); ++clc;
        l ^= type * lans;
        r ^= type * lans;
        if (l > r) swap(l, r);
        if (bel[l] == bel[r]) {
            last1 = last2 = 0; lans = 0;
            for (int i = r; i >= l; i--) {
                ExtendFront(i, r);
                if (pre[last2] != clc) {
                    ++lans; pre[last2] = clc;
                }
            }
        } else {
            lans = ans[bel[l]][r];
            last2 = pf[bel[l]][r];
            for (int i = bel[l] * B; i >= l; i--) {
                ExtendFront(i, r);
                if (pre[last2] != clc) {
                    pre[last2] = clc;
                    if (pt[bel[l]][last2] > r) ++lans;
                }
            }
        }
        printf("%d\n", lans);
    }
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值