BZOJ 2555: SubString 后缀自动机+LCT

10 篇文章 0 订阅
4 篇文章 0 订阅

后缀自动机的经典模型,查询一个字符串出现了几次,我们可以联想一下在代码中并未体现的right数组,我们很容易发现,有几个right数组就代表着这个单词出现了几次,那么我们只要设法维护出来就好了,我们可以想到加一个单词在parent树中只对这个单词所在的链有影响,所以我们只要维护一个LCT把parent树维护出来就行了,每次新加节点将parent树对应的链的right+1就行了(据说这题不用LCT更快)

#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cstring>
#include<string>
#include<iostream>
#include<iomanip>
#include<algorithm>
using namespace std;
struct splay
{
    splay *fa,*ls,*rs;
    int add_mark,val;
    splay(int);
    void push_down();
    void add(int v);
}*null=new splay(0);
splay :: splay(int _=0)
{
    fa=ls=rs=null;
    add_mark=0;
    val=_;
}
void splay :: add(int v)
{
    val+=v;
    add_mark+=v;
}
void splay :: push_down()
{
    if(add_mark)
    {
        if(ls!=null) ls->add(add_mark);
        if(rs!=null) rs->add(add_mark);
        add_mark=0;
    }
}
void right(splay *x)
{
    splay *y=x->fa;
    y->ls=x->rs;
    x->rs->fa=y;
    x->fa=y->fa;
    x->rs=y;
    if(y==y->fa->ls) y->fa->ls=x;
    else if(y==y->fa->rs) y->fa->rs=x;
    y->fa=x;
}
void left(splay *x)
{
    splay *y=x->fa;
    y->rs=x->ls;
    x->ls->fa=y;
    x->fa=y->fa;
    x->ls=y;
    if(y==y->fa->ls) y->fa->ls=x;
    else if(y==y->fa->rs) y->fa->rs=x;
    y->fa=x;
}
void push_down(splay *x)
{
    if(x==x->fa->ls || x==x->fa->rs) push_down(x->fa);
    x->push_down();
}
void splaying(splay *x)
{
    push_down(x);
    while(1)
    {
        splay *y=x->fa;
        splay *z=y->fa;
        if(x!=y->ls && x!=y->rs) break;
        if(y!=z->ls && y!=z->rs)
        {
            if(x==y->ls) right(x);
            else left(x);
            break;
        }
        if(x==y->ls)
        {
            if(y==z->ls) right(y);
            right(x);
        }
        else if(x==y->rs)
        {
            if(y==z->rs) left(y);
            left(x);
        }
    }
}
void access(splay *x)
{
    splay *y=null;
    while(x!=null)
    {
        splaying(x);
        x->rs=y;
        y=x;
        x=x->fa;
    }
}
void cut(splay *x)
{
    access(x);
    splaying(x);
    x->ls->fa=null;
    x->ls=null;
}
void link(splay *x,splay *y)
{
    cut(x);
    x->fa=y;
}
struct sam
{
    sam *parent,*son[26];
    int max_len;
    splay *tree;
    sam(int _=0):parent(0x0),max_len(_)
    {
        memset(son,0,sizeof(son));
        tree=new splay(0);
    }
}*root=new sam,*last=root;
void my_insert(int x)
{
    sam *p=last;
    sam *np=new sam(p->max_len+1);
    while(p && !p->son[x])
    {
        p->son[x]=np;
        p=p->parent;
    }
    if(!p)
    {
        np->parent=root;
        link(np->tree,root->tree);
    }
    else
    {
        sam *q=p->son[x];
        if(p->max_len==q->max_len-1)
        {
            np->parent=q;
            link(np->tree,q->tree);
        }
        else
        {
            sam *nq=new sam(p->max_len+1);
            nq->parent=q->parent;
            link(nq->tree,q->parent->tree);
            memcpy(nq->son,q->son,sizeof(nq->son));
            q->parent=nq;np->parent=nq;
            link(q->tree,nq->tree);
            link(np->tree,nq->tree);
            push_down(q->tree);
            nq->tree->val=q->tree->val;
            while(p && p->son[x]==q)
            {
                p->son[x]=nq;
                p=p->parent;
            }
        }
    }
    last=np;
    access(np->tree);
    splaying(np->tree);
    np->tree->add(1);
}
char s[1000000];
int search_ans()
{
    sam *o=root;
    for(int i=0;s[i];i++)
    {
        if(!o->son[s[i]-'A']) return 0;
        o=o->son[s[i]-'A'];
    }
    push_down(o->tree);
    return o->tree->val;
}
void main_insert()
{
    for(int i=0;s[i];i++) my_insert(s[i]-'A');
}
void decode(int mask)
{
    int i,n=strlen(s);
    for(i=0;i<n;i++)
    {
        mask=(mask*131+i)%n;
        swap(s[i],s[mask]);
    }
}
int main()
{
    int n;
    scanf("%d",&n);
    scanf("%s",s);
     main_insert();
    int mask=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%s",s);
        if(s[0]=='A')
        {
            scanf("%s",s);
            decode(mask);
            main_insert();
        }
        else if(s[0]=='Q')
        {
            scanf("%s",s);
            decode(mask);
            int ans=search_ans();
            mask^=ans;
            printf("%d\n",ans);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值