bzoj 2555: SubString 后缀自动机+lct

题目大意:
给定一个初始字符串 s s s,要求支持两种操作:
1.给定一个字符串 t t t,字符串 s s s后面加一个字符串 t t t
2.给定一个字符串 t t t,询问 s s s有多少个子串是 t t t
强制在线。
s s s最大长长度 ≤ 6 ∗ 1 0 5 ≤6*10^5 6105,询问数 ≤ 1 0 4 ≤10^4 104,询问串总长 ≤ 3 ∗ 1 0 6 ≤3*10^6 3106

分析:
显然匹配是不合适的,复杂度达到 O ( ∣ s ∣ ∗ q ) O(|s|*q) O(sq)
考虑到有字符串总长,可以建出后缀自动机然后在上面跑, r i g h t right right集大小为解,跳的复杂度与总长有关。
r i g h t right right集相当于每一个前缀节点权值为 1 1 1,其他节点权值为 0 0 0,fail树上该点的子树权值和。
可以使用lct维护fail树。

代码:

/**************************************************************
    Problem: 2555
    User: ypxrain
    Language: C++
    Result: Accepted
    Time:15476 ms
    Memory:180472 kb
****************************************************************/
 
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
 
const int maxn=1200007;
 
using namespace std;
 
int n,cnt,now,mask;
char s[maxn],op[20];
 
struct node{
    int l,r,fa,rev;
    int sum,data;
}t[maxn];
 
struct sam{
    int fail,len;
    int son[26];
}T[maxn];
 
void updata(int x)
{
    int l=t[x].l,r=t[x].r;
    t[x].sum=t[l].sum+t[r].sum+t[x].data;
}
 
bool isroot(int x)
{
    return (x!=t[t[x].fa].l) && (x!=t[t[x].fa].r);
}
 
void rttr(int x)
{
    int y=t[x].l;
    t[x].l=t[y].r;
    if (t[y].r) t[t[y].r].fa=x;
    if (x==t[t[x].fa].l) t[t[x].fa].l=y;
    else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[y].r=x;
    updata(x); updata(y);
}
 
void rttl(int x)
{
    int y=t[x].r;
    t[x].r=t[y].l;
    if (t[y].l) t[t[y].l].fa=x;
    if (x==t[t[x].fa].l) t[t[x].fa].l=y;
    else if (x==t[t[x].fa].r) t[t[x].fa].r=y;
    t[y].fa=t[x].fa;
    t[x].fa=y;
    t[y].l=x;
    updata(x); updata(y);
}
 
void remove(int x)
{
    if (!isroot(x)) remove(t[x].fa);
    if (t[x].rev)
    {
        t[x].rev^=1;
        swap(t[x].l,t[x].r);
        if (t[x].l) t[t[x].l].rev^=1;
        if (t[x].r) t[t[x].r].rev^=1;
    }
}
 
void splay(int x)
{
    remove(x);
    while (!isroot(x))
    {
        int p=t[x].fa,g=t[p].fa;
        if (isroot(p))
        {
            if (x==t[p].l) rttr(p);
                      else rttl(p);
        }
        else
        {
            if (x==t[p].l)
            {
                if (p==t[g].l) rttr(p),rttr(g);
                          else rttr(p),rttl(g);
            }
            else
            {
                if (p==t[g].l) rttl(p),rttr(g);
                          else rttl(p),rttl(g);
            }
        }
    }
}
 
void access(int x)
{
    int y=0;
    while (x)
    {
        splay(x);
        t[x].data+=t[t[x].r].sum;
        t[x].data-=t[y].sum;
        t[x].r=y;
        updata(x);
        y=x,x=t[x].fa;
    }
}
 
void makeroot(int x)
{
    access(x);
    splay(x);
    t[x].rev^=1;
}
 
void link(int x,int y)
{
    makeroot(x);
    splay(x);
    access(y);
    splay(y);
    t[y].fa=x;
    t[x].data+=t[y].sum;
    updata(x);
}
 
void cut(int x,int y)
{
    makeroot(x);
    access(y);
    splay(y);
    t[x].fa=0,t[y].l=0;
    updata(y);
}
 
void build_sam()
{
    int p,q,clone;
    int L=strlen(s);
    for (int i=0;i<L;i++)
    {
        int c=s[i]-'A';
        p=now;
        now=++cnt;
        T[now].len=T[p].len+1;
        t[now].data=t[now].sum=1;
        while (p&&(!T[p].son[c])) T[p].son[c]=now,p=T[p].fail;
        if (!p) T[now].fail=1,link(1,now);
        else
        {
            q=T[p].son[c];
            if (T[p].len+1==T[q].len) T[now].fail=q,link(q,now);
            else
            {
                clone=++cnt;
                T[clone]=T[q];
                T[clone].len=T[p].len+1;
                cut(T[q].fail,q);
                link(T[q].fail,clone);
                T[now].fail=T[q].fail=clone;
                link(clone,q);
                link(clone,now);
                while (p&&(T[p].son[c]==q)) T[p].son[c]=clone,p=T[p].fail;
            }
        }
    }
}
 
void getstr(int mask)
{
    int L=strlen(s);
    for (int j=0;j<L;j++)
    {
        mask=(mask*131+j)%L;
        swap(s[j],s[mask]);
    }
}
 
int getans()
{
    int L=strlen(s);
    int p=1;
    for (int i=0;i<L;i++)
    {
        int c=s[i]-'A';
        if (!T[p].son[c]) return 0;
        p=T[p].son[c];
    }
    makeroot(1);
    access(p);
    return t[p].data;
}
 
int main()
{
    scanf("%d",&n);
    scanf("%s",s);
    cnt=now=1;
    build_sam();    
    for (int i=1;i<=n;i++)
    {
        scanf("%s",op);
        scanf("%s",s);      
        getstr(mask);
        if (op[0]=='A') build_sam();
        else
        {
            int d=getans();
            mask^=d;
            printf("%d\n",d);
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值