BZOJ2555:SubString 后缀自动机 LCT

本文介绍如何使用后缀自动机与链剖树(LCT)解决字符串匹配问题,包括在字符串末尾添加新字符串及查询子串出现次数。通过LCT维护节点大小,实现高效查询。

给你一个字符串init,要求你支持两个操作
(1):在当前字符串的后面插入一个字符串
(2):询问字符串s在当前字符串中出现了几次?(作为连续子串)
你必须在线支持这些操作。
字符串最终长度 <= 600000,询问次数<= 10000,询问总长度<= 3000000
一个子串出现的次数等于后缀自动机的parent树(也就是反串的后缀树)上这个串对应的节点集合内元素的数量。由parent树的性质可知,这个数量等于子树内反前缀节点的数量。因此对parent树维护LCT,每次新加一个节点后,将这个节点到根的路径size加一,查询时找到需要的节点用LCT查询size值即可。
需要注意的是,nq节点拷贝q节点时要连着size值一起复制。

#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
static char pool[158400000];
struct node
{
    //LCT elements
    node *s[2],*f;
    int add,v;
    inline char r(){return this==f->s[0]?0:this==f->s[1]?1:-1;}
    inline void plus(int x=1){add+=x,v+=x;}
    inline void down(){if(~r()) f->down();if(add){s[0]->plus(add),s[1]->plus(add),add=0;}}
    inline void setson(node* p){p->f=p->pa=this;}
    inline void cutf();
    inline int size();
    inline void mark();
    //SAM elements
    node *trans[26],*pa;
    int d;
    node(int dep=0);
    inline void* operator new(size_t);
    inline void copy(node *x)
    {
        v=x->v;
        memcpy(trans,x->trans,sizeof trans);
    }
}*nil=new node;
node::node(int dep):f(nil),add(0),v(0),trans(),pa(),d(dep){s[0]=s[1]=nil;}
inline void* node::operator new(size_t)
{
    static node* s=(node*)pool;
    static size_t t=-1;
    return s+ ++t;
}
inline void rot(node *x)
{
    static node *o,*y;
    static char k;
    k=x->r();if(k==-1) return;
    k=!k;
    o=x->f,y=x->s[k];
    x->s[k]=o,o->s[!k]=y;
    if(~(k=o->r())) o->f->s[k]=x;
    x->f=o->f,o->f=x,y->f=o;
}
inline void splay(node *x)
{
    x->down();
    while(~x->r()) rot(x->r()==x->f->r()?x->f:x),rot(x);
}
inline void __access(node *x)
{
    static node *y;
    y=nil;
    while(x!=nil)
    {
        splay(x);
        x->s[1]=y;
        y=x;
        x=x->f;
    }
}
inline void access(node *x)
{
    __access(x),splay(x);
}
inline void chain_add(node *x)
{
    access(x);
    x->plus();
}
inline int getsum(node *x)
{
    splay(x);
    return x->v;
}
inline void cut(node *x)
{
    access(x);
    x->s[0]=x->s[0]->f=nil;
}
inline void node::mark()
{
    chain_add(this);
}
inline int node::size()
{
    return getsum(this);
}
inline void node::cutf()
{
    cut(this);
}
struct sam
{
    node *rt,*last;
    sam():rt(new node),last(rt){}
    inline void push_back(char c)
    {
        node *p,*q,*np,*nq;
        c-='A';
        p=last;
        np=last=new node(p->d+1);
        while(p&&!p->trans[c]) p->trans[c]=np,p=p->pa;
        if(!p) {rt->setson(np);goto finish;}
        q=p->trans[c];
        if(p->d+1==q->d) {q->setson(np);goto finish;}
        nq=new node(p->d+1);
        q->pa->setson(nq);q->cutf();
        nq->copy(q);
        nq->setson(q);nq->setson(np);
        while(p&&p->trans[c]==q) p->trans[c]=nq,p=p->pa;
        finish:
        np->mark();
    }
    inline size_t count(const char *s)
    {
        node *now=rt;
        while(*s)
        {
            now=now->trans[(*s++)-'A'];
            if(!now) return 0;
        }
        return now->size();
    }
}tr;
inline void unzip(char *s,int mask)
{
    int len=strlen(s);
    for(int i=0;i<len;++i)
    {
        mask=(mask*131+i)%len;
        char t=s[i];
        s[i]=s[mask];
        s[mask]=t;
    }
}
char s[3000001],tp[6];
int q,mask=0;
int main()
{
    scanf("%d",&q);
    scanf("%s",s);
    for(int i=0;s[i];++i)
    tr.push_back(s[i]);
    while(q--)
    {
        scanf("%s%s",tp,s);
        unzip(s,mask);
        if(*tp=='A')
        {
            for(int i=0;s[i];++i)
            tr.push_back(s[i]);
        }
        else
        {
            size_t ans=tr.count(s);
            mask^=ans;
            printf("%u\n",ans);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值