洛谷 P4070 [SDOI2016]生成魔咒 后缀自动机

本文介绍了一道关于后缀自动机的经典算法题目,详细解释了如何通过构建后缀自动机来解决魔咒串的生成魔咒计数问题,并提供了完整的C++实现代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目描述

魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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 共有多少种生成魔咒。

输入输出格式

输入格式:
第一行一个整数 nn

第二行 n 个数,第 ii 个数表示第 i 次操作加入的魔咒字符。

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

输入输出样例

输入样例#1:
7
1 2 3 3 3 1 2
输出样例#1:
1
3
6
9
12
17
22
说明

对于%10的数据,1n101≤n≤10
对于%30的数据,1n1001≤n≤100
对于%60的数据,1n1001≤n≤100
对于%100的数据,1n1000001≤n≤100000
用来表示魔咒字符的数字 xx 满足1n109

分析:
显然是一道后缀自动机的模板题,每加入一个点的答案,就是新建的这个点的lenlen集大小。
也就是t[now].lent[t[now].fail].lent[now].len−t[t[now].fail].len,因为这些字符的rightright集都为11,而且是以当前点为结束的点。

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <map>
#define LL long long

const int maxn=2e5+7;

using namespace std;

int n,cnt;
int a[maxn];
LL ans;

struct node{
    int len,fail;
    map <int,int> son;
}t[maxn];

void build_sam()
{
    cnt=1;
    int now=1,p,q,clone;
    for (int i=1;i<=n;i++)
    {
        int c=a[i];
        p=now;
        now=++cnt;
        t[now].len=t[p].len+1;
        while (p&&(!t[p].son[c])) t[p].son[c]=now,p=t[p].fail;
        if (!p) t[now].fail=1;
        else
        {
            q=t[p].son[c];
            if (t[p].len+1==t[q].len) t[now].fail=q;
            else
            {
                clone=++cnt;
                t[clone]=t[q];
                t[clone].len=t[p].len+1;
                t[now].fail=t[q].fail=clone;
                while (p&&(t[p].son[c]==q)) t[p].son[c]=clone,p=t[p].fail;
            }
        }
        ans+=t[now].len-t[t[now].fail].len;
        printf("%lld\n",ans);
    }
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    build_sam();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值