[bzoj4516][SAM]生成魔咒

8 篇文章 0 订阅

Description

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 1、2 拼凑起来形成一个魔咒串 [1,2]。 一个魔咒串 S
的非空字串被称为魔咒串 S 的生成魔咒。 例如 S=[1,2,1] 时,它的生成魔咒有
[1]、[2]、[1,2]、[2,1]、[1,2,1] 五种。S=[1,1,1] 时,它的生成魔咒有 [1]、 [1,1]、[1,1,1]
三种。最初 S 为空串。共进行 n 次操作,每次操作是在 S 的结尾加入一个魔咒字符。每次操作后都 需要求出,当前的魔咒串 S
共有多少种生成魔咒。

Input

第一行一个整数 n。 第二行 n 个数,第 i 个数表示第 i 次操作加入的魔咒字符。 1≤n≤100000。,用来表示魔咒字符的数字 x
满足 1≤x≤10^9

Output

输出 n 行,每行一个数。第 i 行的数表示第 i 次操作后 S 的生成魔咒数量

Sample Input

7

1 2 3 3 3 1 2

Sample Output

1

3

6

9

12

17

22

题解

今天仔细想了想SAM的这条性质
状态u表示的本质不同的字符串共有max[u]-min[u]+1个
我觉得很对,于是就A了
因为每次插入只会多length(S)+1个子串,S表示未插入之前的母串
那么我们只用考虑状态u,他表示的本质不同的子串有多少个就行了

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<map>
using namespace std;
typedef long long LL;
struct SAM
{
    int dep,parent;
    map<int,int> son;
}tr[210000];int cnt,last,root;
LL ans;
void add(int x,int op)
{
    cnt++;int np=cnt;
    int p=last;
    tr[np].dep=op;
    while(p && tr[p].son[x]==0)tr[p].son[x]=np,p=tr[p].parent;
    if(p==0)tr[np].parent=root;
    else
    {
        int q=tr[p].son[x];
        if(tr[q].dep==tr[p].dep+1)tr[np].parent=q;
        else
        {
            int nq=++cnt;
            tr[nq]=tr[q];tr[nq].dep=tr[p].dep+1;
            tr[q].parent=tr[np].parent=nq;
            while(p && tr[p].son[x]==q)tr[p].son[x]=nq,p=tr[p].parent;
        }
    }
    last=np;
    ans+=(LL)tr[np].dep-tr[tr[np].parent].dep;
}
int main()
{
    int n;
    scanf("%d",&n);
    ans=0;root=last=++cnt;
    for(int i=1;i<=n;i++)
    {
        int x;scanf("%d",&x);
        add(x,i);
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值