• 【BZOJ5084】hashit(后缀自动机水过)


    点此看题面

    大致题意: 每次在字符串后面加入或删除一个字符,求本质不同的子串个数。

    后缀自动机

    先说明,此题后缀自动机的确能过。

    但我的后缀自动机比较弱,遇上一个较强的(Hack)数据就被卡掉了。。。(可见这场比赛的(T1)【HHHOJ】ZJOI2019模拟赛(十三)03.10

    尽管如此,还是讲一下大致思路吧。

    更新答案

    考虑后缀自动机求解本质不同的子串个数时,我们需要统计(Len_x-Len_{fa_x})

    所以我们可以简单定义一个(F5)函数来刷新答案:

    #define F5(x,op) (void)(ans+=1LL*(op)*(O[x].L-O[GetFa(x)].L))
    

    插入操作

    考虑后缀自动机的插入过程,对于一个字符,我们可能会新建(1sim2)个节点,且其中第一个节点需要建立于上一个字符第一个节点的基础之上,而第二个节点是作为辅助节点。

    注意在插入的同时要更新(ans)

    由于每次插入需要上一个插入的节点信息,因此我们需要开个栈,来存储还在字符串中的字符编号。

    删除操作

    考虑删除,每次最多只需删除两个节点。

    我们可以标记被删除的节点(Ex=0),存在的节点(Ex=1),然后记录每个节点的后继(Nxt),求的时候跳(Nxt)并路径压缩一下即可。

    删除一个节点后,其所有子节点都变成了它父亲的儿子,因此要将(Sz_{fa_x})先减去(1),再加上(Sz_x),即加上(Sz_x-1)

    更新贡献时,设这个节点为(x),若要减去它的贡献,就是(F5(x,-1))

    但删去它之后,它所有子节点与其父节点(L)的差值都增加了(Len_x-Len_{fa_x}),也就相当于(F5(x,Sz_x))

    合起来便是(F5(x,Sz_x-1))

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define LL long long
    using namespace std;
    char s[N+5];
    class SuffixAutomation//后缀自动机
    {
        private:
            #define F5(x,op) (void)(ans+=1LL*(op)*(O[x].L-O[GetFa(x)].L))//更新答案
            #define Co(x,y) (void)(++O[O[x].F=y].Sz)//连边
            static const int SZ=N,C=26;int tot;LL ans;
            struct SAM {int L,F,Sz,Ex,Nxt,S[C+5];}O[(SZ<<1)+5];
            I int GetFa(CI x) {return O[O[x].F].Ex?O[x].F:O[x].F=GetFa(O[x].F);}//求第一个存在的父亲
            I int GetNxt(int& x) {return O[x].Ex?x:x=GetNxt(O[x].Nxt);}//求出第一个存在的后继
        public:
            int ExSt[N+5],SamP[N+5],TwoP[N+5];//ExSt为一个记录存在节点的栈,SamP和TwoP分别存储一个字符在SAM中建的两个节点的编号
            I SuffixAutomation() {O[0].Ex=O[SamP[0]=tot=1].Ex=1;}//初始化后缀自动机
            I void Insert(CI x,CI id,CI lst)//插入字符
            {
                RI p=lst,q,k,now=SamP[id]=++tot;O[now].L=O[p].L+1,O[now].Ex=1;
                W(p&&!GetNxt(O[p].S[x])) O[p].S[x]=now,p=O[p].F;
                if(!p) return Co(now,1),F5(now,1);
                if(O[p].L+1==O[q=O[p].S[x]].L) return Co(now,q),F5(now,1);
                O[k=TwoP[id]=++tot]=O[q],O[k].L=O[p].L+1,O[k].Sz=0,O[k].Ex=1,O[k].Nxt=q,
                F5(q,-1),Co(now,k),Co(q,k),F5(q,1),F5(k,1),F5(now,1);//删除原来的贡献,更新新的贡献
                W(p&&!(GetNxt(O[p].S[x])^q)) O[p].S[x]=k,p=O[p].F;
            }
            I void Delete(CI x) {O[x].Ex=0,F5(x,O[x].Sz-1),O[GetFa(x)].Sz+=O[x].Sz-1;}//删除字符
            I LL GetAns() {return ans;}//求答案
    }S;
    int main()
    {
        RI i,len,x,T=0;for(scanf("%s",s+1),len=strlen(s+1),i=1;i<=len;++i)
        {
            if(s[i]^'-') S.Insert(s[i]&31,i,S.SamP[S.ExSt[T]]),S.ExSt[++T]=i;//加入字符,更新栈
            else//删除字符
            {
                S.TwoP[S.ExSt[T]]&&(S.Delete(S.TwoP[S.ExSt[T]]),0),//若插入了两个节点,则删除第二个插入的节点
                S.Delete(S.SamP[S.ExSt[T]]),--T;//删除第一个插入的节点
            }printf("%lld
    ",S.GetAns());//输出答案
        }return 0;
    }
    
  • 相关阅读:
    spring boot启动原理
    Minor GC ,Full GC 触发条件是什么?
    Redis分布式锁的正确实现方式Jedis客户端实现方式总结
    JVM命令总结
    InnoDB中B+树总结
    ThreadPoolExecutor 优先级的线程池
    锁的概述
    Deadlock Troubleshooting, Part 1
    Deadlock Troubleshooting, Part 3
    大型网站数据库优化和故障跟踪与排查(下篇)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ5084.html
Copyright © 2020-2023  润新知