後綴自動機 + LCT 【bzoj2555】SubString

題目大意:
支持兩個操作:
1、插入一串字符
2、查詢一串字符在串中出現多少次。
強制在線

題目分析:
維護一個動態的字符串,後綴自動機可以做。
要查詢一串字符在串中出現的次數,就相當於查詢這個串末尾字符代表的狀態的right集合的大小,但是我們在維護後綴自動機的時候沒辦法維護right集合呀!
但是我們可以發現,一個節點的right集合等於在parent樹上所有兒子節點的和。
所以只要維護一下所有兒子節點的和就可以了。
因爲每次只插入一個點,那就把到根節點路徑上的所有節點的right集合都+1。
但是我們要動態的更改和查詢這顆樹上的點,也就是說這棵樹是一顆動態樹,那就用LCT來維護一下,在新建節點的時候,和parent樹上的父親Link一下就可以,然後把新建的節點到根的路徑整體+1。
(是據說其實暴力更改更快???)

代碼如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <fstream>
#include <iostream>
using namespace std;
int Q,mask;
char opt[20],s[3000001];
struct splay{
    splay *ch[2],*fa;
    int sum,mark;
    splay();
    void add_mark(int mark);
    void push_down();
    void push_up();
    int dir();
}*null=new splay;
splay :: splay()
{
    sum=mark=0;
    ch[0]=ch[1]=fa=null;
}
void splay :: add_mark(int v)
{
    if(this==null) return;
    sum+=v;
    mark+=v;
    return;
}
void splay :: push_down()
{
    if(mark)
    {
        ch[0]->add_mark(mark);
        ch[1]->add_mark(mark);
        mark=0;
    }
    return;
}
void splay :: push_up()
{
    if(~dir()) fa->push_up();
    push_down();
    return;
}
int splay :: dir()
{
    return fa->ch[0]==this?0:fa->ch[1]==this?1:-1;
}
void turn(splay *c,int d)
{
    splay *y=c->ch[d^1];
    c->ch[d^1]=y->ch[d];
    if(y->ch[d]!=null) y->ch[d]->fa=c;
    y->fa=c->fa;
    y->ch[d]=c;
    int k;
    if(~(k=c->dir())) c->fa->ch[k]=y;
    c->fa=y;
    return;
}
void splaying(splay *c)
{
    c->push_up();
    int d;
    while(~(d=c->dir()))
    {
        if(d==c->fa->dir()) turn(c->fa->fa,d^1);
        turn(c->fa,d^1);
    }
    return;
}
void Access(splay *c)
{
    splay *y=null;
    while(c!=null)
    {
        splaying(c);
        c->ch[1]=y;
        y=c;
        c=c->fa;
    }
    return;
}
void Cut(splay *x)
{
    Access(x),splaying(x);
    x->ch[0]->fa=null;
    x->ch[0]=null;
    return;
}
void Link(splay *x,splay *y)
{
    Cut(y);
    y->fa=x;
    return;
}
struct SAM{
    SAM *son[26],*fa;
    splay *tree;
    int max_len;
    SAM(int _);
}*root=new SAM(0),*last=root;
SAM :: SAM(int _):max_len(_)
{
    memset(son,0,sizeof(son));
    fa=NULL;
    tree=new splay;
}
void extend(int x)
{
    SAM *p=last;
    SAM *np=new SAM(p->max_len+1);
    while(p && !p->son[x]) p->son[x]=np,p=p->fa;
    if(p==NULL) np->fa=root;
    else
    {
        SAM *q=p->son[x];
        if(p->max_len+1==q->max_len) np->fa=q;
        else
        {
            SAM *nq=new SAM(p->max_len+1);
            nq->fa=q->fa;
            q->tree->push_up();
            nq->tree->sum=q->tree->sum;
            Link(nq->tree,q->tree);
            Link(q->fa->tree,nq->tree);
            memcpy(nq->son,q->son,sizeof(nq->son));
            q->fa=nq; np->fa=nq;
            for(;p&&p->son[x]==q;p=p->fa) p->son[x]=nq;
        }
    }
    last=np;
    Link(np->fa->tree,np->tree);
    Access(np->tree),splaying(np->tree);
    np->tree->add_mark(1);
}
void translate(int mask)
{
    static int len;
    len=strlen(s);
    for(int j=0;j<len;j++)
    {
        mask=(mask*131+j)%len;
        swap(s[j],s[mask]);
    }
}
int query()
{
    SAM *c=root;
    for(int i=0;s[i];i++)
    {
        if(!c->son[s[i]-'A']) return 0;
        c=c->son[s[i]-'A'];
    }
    c->tree->push_up();
    return c->tree->sum;
}
int main()
{
    scanf("%d%s",&Q,s);
    for(int i=0;s[i];i++) extend(s[i]-'A');
    while(Q--)
    {
        scanf("%s%s",opt,s);
        translate(mask);
        switch(opt[0])
        {
        case 'A':
            for(int i=0;s[i];i++) extend(s[i]-'A');
            break;
        case 'Q':
            int ans=query();
            printf("%d\n",ans);
            mask^=ans;
            break;
        }
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章