1. 程式人生 > >P5212 SubString LCT+SAM

P5212 SubString LCT+SAM

static end 匹配 要求 init 一行 this getchar char

$ \color{#0066ff}{ 題目描述 }$

給定一個字符串init,要求支持兩個操作

  • 在當前字符串的後面插入一個字符串
  • 詢問字符串ss在當前字符串中出現了幾次?(作為連續子串)

強制在線。

\(\color{#0066ff}{輸入格式}\)

第一行一個整數\(Q\)表示操作個數

第二行一個字符串表示初始字符串init

接下來Q行,每行2個字符串Type,Str

  • TypeADD,表示在後面插入字符串。
  • TypeQUERY,表示詢問某字符串在當前字符串中出現了幾次。

為了體現在線操作,你需要維護一個變量mask,初始值為00

技術分享圖片

讀入串Str之後,使用這個過程將之解碼成真正詢問的串TrueStr

詢問的時候,對TrueStr詢問後輸出一行答案Result

然後\(mask=mask \bigoplus Result\)

插入的時候,將TrueStr插到當前字符串後面即可。

註意:ADD和QUERY操作的字符串都需要解壓

\(\color{#0066ff}{輸出格式}\)

對於每一個QUERY操作,輸出詢問的字符串在當前字符串中出現了幾次。

\(\color{#0066ff}{輸入樣例}\)

2
A
QUERY B
ADD BBABBBBAAB

\(\color{#0066ff}{輸出樣例}\)

0

\(\color{#0066ff}{數據範圍與提示}\)

\(∣S∣≤6×10^5,Q \leq 10^4\)

,詢問總長度\(\leq 3 \times 10^6\)

為防止評測過慢,對於測試點2 3 5 6 8 11 時限為3s,其余為1s

\(\color{#0066ff}{題解}\)

每次插入字符,還要匹配, 顯然SAM再合適不過

匹配的時候,找到那個點\(O(len)\),那麽答案就是parent樹的子樹大小

但是這個樹是動態的。。,於是。。。。LCT啊。。

LCT維護子樹和即可

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
    char ch; LL x = 0, f = 1;
    while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
    for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
    return x * f;
}
const int maxn = 6e6 + 1;
const int maxm = 1.2e6 + 1;
struct LCT {
protected:
    struct node {
        node *ch[2], *fa;
        int tot, siz, val, rev;
        node(int tot = 0, int siz = 0, int val = 0, int rev = 0): tot(tot), siz(siz), val(val), rev(rev) {}
        void trn() { std::swap(ch[0], ch[1]), rev ^= 1; }
        void dwn() {
            if(!rev) return;
            if(ch[0]) ch[0]->trn();
            if(ch[1]) ch[1]->trn();
            rev = 0;
        }
        void upd() {
            tot = siz + val;
            if(ch[0]) tot += ch[0]->tot;
            if(ch[1]) tot += ch[1]->tot;
        }
        bool isr() { return fa->ch[1] == this; }
        bool ntr() { return fa && (fa->ch[1] == this || fa->ch[0] == this); }
    }pool[maxm];
    void rot(node *x) {
        node *y = x->fa, *z = y->fa;
        bool k = x->isr(); node *w = x->ch[!k];
        if(y->ntr()) z->ch[y->isr()] = x;
        (x->ch[!k] = y)->ch[k] = w;
        (y->fa = x)->fa = z;
        if(w) w->fa = y;
        y->upd(), x->upd();
    }
    void splay(node *o) {
        static node *st[maxm];
        int top;
        st[top = 1] = o;
        while(st[top]->ntr()) st[top + 1] = st[top]->fa, top++;
        while(top) st[top--]->dwn();
        while(o->ntr()) {
            if(o->fa->ntr()) rot(o->isr() ^ o->fa->isr()? o : o->fa);
            rot(o);
        }
    }
    void access(node *x) {
        for(node *y = NULL; x; x = (y = x)->fa) {
            splay(x);
            if(x->ch[1]) x->siz += x->ch[1]->tot;
            if((x->ch[1] = y)) x->siz -= x->ch[1]->tot;
            x->upd();
        }
    }
    void makeroot(node *x) { access(x), splay(x), x->trn(); }
    void link(node *x, node *y) {
        makeroot(x), access(y), splay(y);
        (x->fa = y)->siz += x->tot;
        y->upd();
    }
    void cut(node *x, node *y) {
        makeroot(y), access(x), splay(x);
        assert(x->ch[0] == y);
        x->ch[0] = y->fa = NULL, x->upd();
    }
public:
    friend struct SAM;
}c;
struct SAM {
protected: 
    struct node {
        node *ch[26], *fa;
        int len, siz;
        node(int len = 0, int siz = 0): len(len), siz(siz) {}
    }pool[maxm];
    node *root, *tail, *lst;
    LCT::node *id(node *x) { return c.pool + (x - pool); }
    void extend(int s) {
        node *o = new(tail++) node(lst->len + 1, 1), *v = lst;
        id(o)->val = 1, id(o)->upd();
        for(; v && !v->ch[s]; v = v->fa) v->ch[s] = o;
        if(!v) o->fa = root, c.link(id(o), id(root));
        else if(v->len + 1 == v->ch[s]->len) o->fa = v->ch[s], c.link(id(o), id(v->ch[s]));
        else {
            node *n = new(tail++) node(v->len + 1), *d = v->ch[s];
            std::copy(d->ch, d->ch + 26, n->ch);
            id(n)->upd();
            c.cut(id(d), id(d->fa));
            c.link(id(n), id(d->fa));
            c.link(id(d), id(n));
            c.link(id(o), id(n));
            n->fa = d->fa, d->fa = o->fa = n;
            for(; v && v->ch[s] == d; v = v->fa) v->ch[s] = n;
        }
        lst = o;
    }
    void clr() {
        tail = pool;
        root = lst = new(tail++) node();
    }
public:
    SAM() { clr(); }
    void ins(char *s) { for(char *p = s; *p; p++) extend(*p - 'A'); }
    int getans(char *s) {
        node *o = root;
        for(char *p = s; *p; p++) {
            int pos = *p - 'A';
            if(o->ch[pos]) o = o->ch[pos];
            else return 0;
        }
        c.makeroot(id(root));
        c.access(id(o));
        c.splay(id(o));
        return id(o)->val + id(o)->siz;
    }
}s;
char ls[maxn];
int n, len;
void doit(int ans) {
    for(int i = 0; i < len; i++) {
        ans = (ans * 131 + i) % len;
        std::swap(ls[i], ls[ans]);
    }
}
int main() {
    n = in();
    scanf("%s", ls);
    s.ins(ls);
    int mask = 0, ans;
    while(n --> 0) {
        scanf("%s", ls);
        if(ls[0] == 'A') {
            scanf("%s", ls);
            len = strlen(ls);
            doit(mask);
            s.ins(ls);
        }
        else {
            scanf("%s", ls);
            len = strlen(ls);
            doit(mask);
            printf("%d\n", ans = s.getans(ls));
            mask ^= ans;
        }
    }
    return 0;
}

P5212 SubString LCT+SAM