BZOJ4516 SDOI2016 生成魔咒 后缀自动机
题意:初始时给定一个空串,在其后不断加字符,求每次加入后不同子串的数量
题解:
当时拿到这个题一眼看出用后缀数组啥的,然而我并不会QAQ……不耸,不会后缀数据结构我们会HASH是不是,于是用HASH水了30分,能进R2真是命大2333
讲题的时候标算用的后缀数组,不过看他写了半个黑板就感觉好麻烦。YTS大神说这题是个后缀自动机模板题,瞬间感觉自动机啥的都好厉害,于是回来后补了个后缀自动机,果然是个模板题……
首先需要离散化,不然跑不了自动机,这个用map随便搞一搞就好。对于一个状态,其所包含的不同的子串的数目为|right|,而由于我们记录的是max(r),因此|right|=当前点的max(r)-父结点的max(r)。然后把所有状态的|right|全加起来就是答案。
最后别忘了开long long,就这样WA了一发……
#include <map> #include <cstdio> #include <cstring> #include <cstdlib> #include <climits> #include <iostream> #include <algorithm> using namespace std; #define ll long long const int MAXN=100000+2; struct SAM{ int v; map<int,SAM *> child; SAM *f; SAM(int _v):v(_v),f(0){ child.clear();} }*root,*last=root=new SAM(0); int N,x; ll ans; ll Query(SAM *x){ return x->v-x->f->v;} void Extend(int x){ SAM *p=last,*np=new SAM(p->v+1); while(p && !p->child[x]) p->child[x]=np,p=p->f; if(!p) np->f=root,ans+=Query(np); else{ SAM *q=p->child[x]; if(q->v==p->v+1) np->f=q,ans+=Query(np); else{ SAM *nq=new SAM(p->v+1); nq->child=q->child; nq->f=q->f,ans+=Query(nq); np->f=nq,ans+=Query(np); ans-=Query(q),q->f=nq,ans+=Query(q); while(p && p->child[x]==q) p->child[x]=nq,p=p->f; } } last=np; } int main(){ scanf("%d",&N); while(N--){ scanf("%d",&x); Extend(x); printf("%lld\n",ans); } return 0; }