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

版权声明:2333 https://blog.csdn.net/liangzihao1/article/details/82668770

题目描述

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

输入输出格式

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

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

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

输入输出样例

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

对于%10的数据, 1 n 10
对于%30的数据, 1 n 100
对于%60的数据, 1 n 100
对于%100的数据, 1 n 100000
用来表示魔咒字符的数字 x 满足 1 n 10 9

分析:
显然是一道后缀自动机的模板题,每加入一个点的答案,就是新建的这个点的 l e n 集大小。
也就是 t [ n o w ] . l e n t [ t [ n o w ] . f a i l ] . l e n ,因为这些字符的 r i g h t 集都为 1 ,而且是以当前点为结束的点。

代码:

#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();
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/82668770