BZOJ2555:SubString (后缀自动机+(Splay+DFS序/LCT))

8 篇文章 0 订阅
2 篇文章 0 订阅

题目传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2555


题目分析:多串匹配单串,强制在线,只在末端插入字符,让我们很容易想到SAM。其实如果做过阿狸的打字机这题的话,再看此题就显得很简单了。正如我之前所说,SAM本质上是将一个串的所有后缀做成AC自动机,并对其状态进行了化简,使其达到线性的时间和空间复杂度。我们将原串的后缀自动机建出来,查询的时候沿着SAM跑,所到达的状态的Right集的大小便是答案。因为所有的r属于Right都逐一对应匹配串在原串的一个结尾位置。要维护Right集的大小,我们可以考虑在每一次查询的时候,将该状态在parent树上所代表的区间Splay出来(注意拆点),然后查看里面叶子节点的个数;或者考虑逆向思维,在parent树上新插入一个叶子节点的时候,将它到根路径上的Right_Size全部+1,查询时O(1)得到答案。
这题还有一个坑点,就是解密的时候全局变量mask的值是不变的,只有Query的时候才会变,我一开始看题的时候很奇怪:mask不是函数里面的局部变量吗,为什么下面又说mask^=Result?交了几次死活WA,看了看网上PoPoQQQ大神的代码,才发现这两个mask不是同一个变量……


CODE(Splay+DFS序):

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<cstdio>
#include<cstdlib>
#include<stdio.h>
#include<algorithm>
using namespace std;

const int maxn=1200010;
const int maxm=30000010;
const int maxc=26;
typedef long long LL;

struct Tnode
{
    int num,Size;
    Tnode *fa,*son[2];
    int Get_d() { return fa->son[1]==this; }
    void Connect(Tnode *P,int d) { (son[d]=P)->fa=this; }
    void Up() { Size=num+(son[0]? son[0]->Size:0)+(son[1]? son[1]->Size:0); }
} tree[maxn<<1];
Tnode *Troot;
int Tcur=-1;

struct State
{
    int R;
    Tnode *st,*ed;
    State *son[maxc],*parent;
} SAM[maxn];
State *Sroot,*last;
int Scur=-1;

LL mask=0;
char op[maxc];
char s[maxm];

int n,len=1;

Tnode *New_node(int val)
{
    Tcur++;
    tree[Tcur].num=tree[Tcur].Size=val;
    tree[Tcur].fa=tree[Tcur].son[0]=tree[Tcur].son[1]=NULL;
    return tree+Tcur;
}

State *New_state(int val,int x)
{
    Scur++;
    SAM[Scur].R=val;
    SAM[Scur].st=New_node(x);
    SAM[Scur].ed=New_node(0);
    SAM[Scur].st->Connect(SAM[Scur].ed,1);
    for (int i=0; i<maxc; i++) SAM[Scur].son[i]=NULL;
    SAM[Scur].parent=NULL;
    return SAM+Scur;
}

void Zig(Tnode *P,Tnode *&tag)
{
    int d=P->Get_d();
    Tnode *F=P->fa;
    if (P->son[!d]) F->Connect(P->son[!d],d);
    else F->son[d]=NULL;
    if (F!=tag) F->fa->Connect(P, F->Get_d() );
    else P->fa=F->fa,tag=P;
    P->Connect(F,!d);
    F->Up();
}

void Splay(Tnode *P,Tnode *&tag)
{
    Tnode *F;
    while (P!=tag)
    {
        F=P->fa;
        if (F!=tag) ( F->Get_d()^P->Get_d() )? Zig(P,tag):Zig(F,tag);
        Zig(P,tag);
    }
    P->Up();
}

Tnode *Find_succ(Tnode *P)
{
    if (P->son[0]) return Find_succ(P->son[0]);
    return P;
}

Tnode *Find_root(Tnode *P)
{
    if (P->fa) return Find_root(P->fa);
    return P;
}

void Add(State *x,State *y)
{
    Splay(x->st,Troot);
    Tnode *succ=Find_succ(Troot->son[1]);
    Splay(succ,Troot->son[1]);

    Tnode *yRoot=Find_root(y->st);
    Splay(y->st,yRoot);
    Troot->son[1]->Connect(yRoot,0);

    Troot->son[1]->Up();
    Troot->Up();
    y->parent=x;
}

Tnode *Find_prev(Tnode *P)
{
    if (P->son[1]) return Find_prev(P->son[1]);
    return P;
}

void Spilit(State *x)
{
    Splay(x->st,Troot);
    Tnode *prev=Find_prev(Troot->son[0]);
    Splay(x->ed,Troot);
    Tnode *succ=Find_succ(Troot->son[1]);
    Splay(prev,Troot);
    Splay(succ,Troot->son[1]);

    Troot->son[1]->son[0]->fa=NULL;
    Troot->son[1]->son[0]=NULL;
    Troot->son[1]->Up();
    Troot->Up();
}

void Build(int x)
{
    int to=s[x]-'A';
    x+=len;
    State *NP=New_state(x,1),*P=last;
    last=NP;
    while ( P && !P->son[to] ) P->son[to]=NP,P=P->parent;
    if (!P) Add(Sroot,NP);
    else
    {
        State *Q=P->son[to];
        if (P->R+1==Q->R) Add(Q,NP);
        else
        {
            State *NQ=New_state(P->R+1,0);
            for (int i=0; i<maxc; i++) NQ->son[i]=Q->son[i];
            Add(Q->parent,NQ);
            Spilit(Q);
            Add(NQ,Q);
            Add(NQ,NP);
            while ( P && P->son[to]==Q ) P->son[to]=NQ,P=P->parent;
        }
    }
}

void Change(int x)
{
    LL slen=strlen(s);
    for (LL i=0; i<slen; i++)
    {
        x=(x*131LL+i)%slen;
        swap(s[i],s[x]);
    }
}

int main()
{
    freopen("c.in","r",stdin);
    freopen("c.out","w",stdout);

    scanf("%d",&n);
    scanf("%s",&s);

    last=Sroot=New_state(0,0);
    Troot=Sroot->st;
    int slen=strlen(s);
    for (int i=0; i<slen; i++) Build(i);
    len+=slen;

    while (n--)
    {
        scanf("%s %s",&op,&s);
        Change(mask);

        int slen=strlen(s);
        if (op[0]=='A')
        {
            for (int i=0; i<slen; i++) Build(i);
            len+=slen;
        }
        else
        {
            State *P=Sroot;
            for (int i=0; i<slen; i++)
            {
                P=P->son[ s[i]-'A' ];
                if (!P) break;
            }

            if (!P) printf("0\n");
            else
            {
                Splay(P->st,Troot);
                Splay(P->ed,Troot->son[1]);
                Tnode *tag=Troot->son[1]->son[0];

                int ans=0;
                if (tag) ans=tag->Size;
                ans+=P->st->num;
                printf("%d\n",ans);
                mask^=(long long)ans;
            }
        }
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值