【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;
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ5084.html