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;
}
View Code

 

posted @ 2017-02-28 23:18  WDZRMPCBIT  阅读(122)  评论(0编辑  收藏  举报