【GDOI2016模拟】识别子串(SAM的简单应用)

【GDOI2016模拟】识别子串
  • \(SAM\)的简单应用。

  • 注意到一个知识:

    • \(SAM\)上的某个节点,我们可以记录它代表在原串中的第几个位置,也就是最右的那个位置。

    • 然后根据理解,\(SAM\)上的某个节点代表的实际上是原串某个前缀的一些后缀.

    • 然后我们知道这个节点可以代表\(Right[i]\sim Right[i] - len[fail[i]]\).

  • 于是我们就可以枚举一些出现次数只为\(1\)的节点,然后就可以判断其在原串中代表的字符串.

  • 随便几颗线段树就好了.

#include <iostream>
#include <cstdio>
#include <cstring>

#define I register int
#define F(i, a, b) for (I i = a; i <= b; i ++)
#define G(i, a, b) for (I i = a; i >= b; i --)
#define mn(a, b) ((a) = (a) < (b) ? (a) : (b))
#define mx(a, b) ((a) = (a) > (b) ? (a) : (b))
#define mec(a, b) memcpy(a, b, sizeof a)
#define Get getchar()

const int N = 3e5, M = 26, T = 10 * N;

using namespace std;

int lens, tot, las, rt, RT, Rt, ans, cnt, s1, s2, s3, len[N], sum[N], fail[N], son[N][M], c[N], a[N], Rig[N];
struct segm { int l, r, v; } tr[T], TR[T], Tr[T]; char ch[N];

void R(I &x) {
    char c = Get ; x = 0 ; I t = 1;
    for (; !isdigit(c); c = Get) t = (c == '-' ? - 1 : t);
    for (; isdigit(c); x = (x << 3) + (x << 1) + c - '0' , c = Get); x *= t;
}
void W(I x) { if (x > 9) W(x / 10); putchar(x % 10 + '0'); }

void Add(I x, I t) {
    len[++ tot] = len[las] + 1, sum[tot] = 1, Rig[tot] = t; I np = tot, p = las;
    for (; p && !son[p][x]; son[p][x] = np, p = fail[p]);
    if (p) { I q = son[p][x];
        if (len[q] > len[p] + 1) {
            len[++ tot] = len[p] + 1, mec(son[tot], son[q]), fail[tot] = fail[q], Rig[tot] = Rig[q];
            fail[q] = fail[np] = tot;
            for (; p && son[p][x] == q; son[p][x] = tot, p = fail[p]);
        } else fail[np] = q;
    } else fail[np] = 1;
    las = np;
}

void update(I &x, I st, I en, I l, I r, I p) {
    if (!x) tr[x = ++ cnt] = {0, 0, 1e9};
    if (l <= st && en <= r) { mn(tr[x].v, p); return; } I m = st + en >> 1;
    if (m >= l) update(tr[x].l, st, m, l, r, p);
    if (m < r) update(tr[x].r, m+1, en, l, r, p);
}
void UPDATE(I &x, I st, I en, I l, I r, I p) {
    if (!x) TR[x = ++ cnt] = {0, 0, -1e9};
    if (l <= st && en <= r) { mx(TR[x].v, p); return; } I m = st + en >> 1;
    if (m >= l) UPDATE(TR[x].l, st, m, l, r, p);
    if (m < r) UPDATE(TR[x].r, m + 1, en, l, r, p);
}
void UpDaTe(I &x, I st, I en, I l, I r, I p) {
    if (!x) Tr[x = ++ cnt] = {0, 0, 1e9};
    if (l <= st && en <= r) { mn(Tr[x].v, p); return; } I m = st + en >> 1;
    if (m >= l) UpDaTe(Tr[x].l, st, m, l, r, p);
    if (m < r) UpDaTe(Tr[x].r, m + 1, en, l, r, p);
}

void find(I x, I st, I en, I p) { if (!x) return; mn(ans, tr[x].v);
    if (st == en) return; I m = st + en >> 1;
    if (m >= p) find(tr[x].l, st, m, p); else find(tr[x].r, m + 1, en, p);
}

void FIND(I x, I st, I en, I p) { if (!x) return; mx(ans, TR[x].v);
    if (st == en) return; I m = st + en >> 1;
    if (m >= p) FIND(TR[x].l, st, m, p); else FIND(TR[x].r, m + 1, en, p);
}

void Find(I x, I st, I en, I p) { if (!x) return; mn(ans, Tr[x].v);
    if (st == en) return; I m = st + en >> 1;
    if (m >= p) Find(Tr[x].l, st, m, p); else Find(Tr[x].r, m + 1, en, p);
}

int main() {
    scanf("%s", ch + 1), lens = strlen(ch + 1), tot = las = 1;
    F(i, 1, lens) Add(ch[i] - 'a', i);

    F(i, 1, tot) c[len[i]] ++;
    F(i, 1, tot) c[i] += c[i - 1];
    F(i, 1, tot) a[c[len[i]] --] = i;
    G(i, tot, 1) sum[fail[a[i]]] += sum[a[i]];
    F(i, 1, tot) if (sum[i] == 1 && Rig[i] - len[fail[i]]) update(rt, 1, lens, Rig[i] - len[fail[i]], Rig[i], len[fail[i]] + 1); cnt = 0;
    F(i, 1, tot) if (sum[i] == 1 && Rig[i] < lens) UPDATE(RT, 1, lens, Rig[i] + 1, lens, Rig[i] - len[fail[i]]); cnt = 0;
    F(i, 1, tot) if (sum[i] == 1 && Rig[i] - len[fail[i]] - 1) UpDaTe(Rt, 1, lens, 1, Rig[i] - len[fail[i]] - 1, Rig[i]);

    F(i, 1, lens) {
        ans = lens, find(rt, 1, lens, i), s1 = ans;
        ans = 1e9, Find(Rt, 1, lens, i), s3 = ans - i + 1;
        ans = -1e9, FIND(RT, 1, lens, i), s2 = ans == -1e9 ? 1e9 : i - ans + 1;
        printf("%d\n", min(min(s1, s2), s3));
    }
}

猜你喜欢

转载自www.cnblogs.com/Pro-king/p/9383494.html