BZOJ4516: [Sdoi2016]生成魔咒

【传送门:BZOJ4516


简要题意:

  给出一个长度为n的数字字符串,求出每个数字插入到字符串结尾时的不同子串个数


题解:

  后缀自动机

  对于一个状态s,他的right集合代表的子串的长度就是(dep[fail],dep[s]]。这道题我们需要动态的维护不同子串的个数,每次从头扫一遍直接计算肯定不行,我们考虑加入一个新字符会产生多少新的不同子串,这个个数其实就是(dep[fail],dep[s]]的区间长度(自行yy吧)

  注意加long long,而且字符种数太多了,用map存


参考代码:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
map<int,int>tr[210000];
int dep[210000],fail[210000],cnt,last,root;
int a[110000];
void add(int k)
{
    int x=a[k];
    int np=++cnt,p=last;
    dep[np]=k;
    while(p!=0&&tr[p][x]==0) tr[p][x]=np,p=fail[p];
    if(p==0) fail[np]=root;
    else
    {
        int q=tr[p][x];
        if(dep[q]==dep[p]+1) fail[np]=q;
        else
        {
            int nq=++cnt;
            tr[nq]=tr[q];
            dep[nq]=dep[p]+1;
            fail[nq]=fail[q];
            fail[q]=fail[np]=nq;
            while(p!=0&&tr[p][x]==q) tr[p][x]=nq,p=fail[p];
        }
    }
    last=np;
}
int Rsort[110000],sa[210000],r[210000],sum[210000];
int main()
{
    int n;
    scanf("%d",&n);
    cnt=last=root=1;
    LL ans=0;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(i);
        ans+=dep[last]-dep[fail[last]];
        printf("%lld\n",ans);
    }
    return 0;
}

 

posted @ 2018-04-12 10:21  Star_Feel  阅读(150)  评论(0编辑  收藏  举报