题目描述
魔咒串由许多魔咒字符组成,魔咒字符可以用数字表示。例如可以将魔咒字符 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 共有多少种生成魔咒。
输入输出格式
输入格式:
第一行一个整数
。
第二行 个数,第 个数表示第 次操作加入的魔咒字符。
输出格式:
输出
行,每行一个数。第
行的数表示第
次操作后
的生成魔咒数量
输入输出样例
输入样例#1:
7
1 2 3 3 3 1 2
输出样例#1:
1
3
6
9
12
17
22
说明
对于%10的数据,
对于%30的数据,
对于%60的数据,
对于%100的数据,
用来表示魔咒字符的数字
满足
分析:
显然是一道后缀自动机的模板题,每加入一个点的答案,就是新建的这个点的
集大小。
也就是
,因为这些字符的
集都为
,而且是以当前点为结束的点。
代码:
#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();
}